From ead9c560c3b1d6569c3a3c639a63c672790566f4 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Wed, 4 Dec 2024 07:24:11 +0200 Subject: [PATCH 01/11] Remove unused ClientApplicationMapper dependency. --- .../controller/ClientApplicationController.java | 3 --- .../service/ClientApplicationServiceTest.java | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java b/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java index ea28be1..33cbc92 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java @@ -20,7 +20,6 @@ import com.helioauth.passkeys.api.generated.models.AddApplicationRequest; import com.helioauth.passkeys.api.generated.models.Application; import com.helioauth.passkeys.api.generated.models.ApplicationApiKey; -import com.helioauth.passkeys.api.mapper.ClientApplicationMapper; import com.helioauth.passkeys.api.service.ClientApplicationService; import lombok.RequiredArgsConstructor; import lombok.val; @@ -48,8 +47,6 @@ public class ClientApplicationController implements ApplicationsApi { private final ClientApplicationService clientApplicationService; - private final ClientApplicationMapper clientApplicationMapper; - @GetMapping @Override public ResponseEntity> listAll() { diff --git a/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java b/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java index bccc604..d22336b 100644 --- a/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java +++ b/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.helioauth.passkeys.api.service; import com.helioauth.passkeys.api.domain.ClientApplication; From b619600c93d049667c0d10406de830d65bad90bc Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Wed, 4 Dec 2024 07:24:25 +0200 Subject: [PATCH 02/11] Add constructor generation options in pom.xml config --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index a425fee..3b51aea 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,8 @@ true false true + true + true com.helioauth.passkeys.api.generated.models com.helioauth.passkeys.api.generated.api From aa32add068c2665f35e45a60b3a3314478e9b98c Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Wed, 4 Dec 2024 07:27:05 +0200 Subject: [PATCH 03/11] Implement generated UserApi interface for some endpoints --- .../passkeys/api/controller/UsersController.java | 13 ++++++++----- .../passkeys/api/mapper/UserCredentialMapper.java | 5 +++-- .../passkeys/api/service/UserCredentialManager.java | 6 ++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java index 548cd1b..848d9fc 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java @@ -16,11 +16,13 @@ package com.helioauth.passkeys.api.controller; +import com.helioauth.passkeys.api.generated.api.UsersApi; +import com.helioauth.passkeys.api.generated.models.ListPasskeysResponse; import com.helioauth.passkeys.api.service.UserAccountManager; import com.helioauth.passkeys.api.service.UserCredentialManager; -import com.helioauth.passkeys.api.service.dto.ListPasskeysResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +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; @@ -36,17 +38,18 @@ @RequestMapping("/v1/users") @Slf4j @RequiredArgsConstructor -public class UsersController { +public class UsersController implements UsersApi { private final UserCredentialManager userCredentialManager; private final UserAccountManager userAccountManager; @GetMapping("/{uuid}/credentials") - public ListPasskeysResponse getUserCredentials(@PathVariable UUID uuid) { - return userCredentialManager.getUserCredentials(uuid); + public ResponseEntity getUserCredentials(@PathVariable UUID uuid) { + return ResponseEntity.ok(userCredentialManager.getUserCredentials(uuid)); } @DeleteMapping("/{uuid}") - public void deleteUser(@PathVariable UUID uuid) { + public ResponseEntity deleteUser(@PathVariable UUID uuid) { userAccountManager.deleteUser(uuid); + return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java b/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java index 20a34af..804a882 100644 --- a/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java +++ b/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java @@ -17,8 +17,9 @@ package com.helioauth.passkeys.api.mapper; import com.helioauth.passkeys.api.domain.UserCredential; +import com.helioauth.passkeys.api.generated.models.PasskeyCredential; +import com.helioauth.passkeys.api.generated.models.SignUpStartResponse; import com.helioauth.passkeys.api.service.dto.CredentialRegistrationResult; -import com.helioauth.passkeys.api.service.dto.PasskeyItem; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingConstants; @@ -30,7 +31,7 @@ */ @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) public interface UserCredentialMapper { - List toDto(List userCredentialList); + List toDto(List userCredentialList); @Mapping(target = "createdAt", ignore = true) @Mapping(target = "lastUsedAt", ignore = true) diff --git a/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java b/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java index 4de3b04..4d37649 100644 --- a/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java +++ b/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java @@ -24,10 +24,9 @@ import com.helioauth.passkeys.api.domain.UserCredential; import com.helioauth.passkeys.api.domain.UserCredentialRepository; import com.helioauth.passkeys.api.domain.UserRepository; +import com.helioauth.passkeys.api.generated.models.ListPasskeysResponse; import com.helioauth.passkeys.api.mapper.UserCredentialMapper; import com.helioauth.passkeys.api.service.dto.CredentialRegistrationResult; -import com.helioauth.passkeys.api.service.dto.ListPasskeysResponse; -import com.helioauth.passkeys.api.service.dto.PasskeyItem; import com.helioauth.passkeys.api.service.exception.CreateCredentialFailedException; import com.helioauth.passkeys.api.service.exception.SignUpFailedException; import lombok.RequiredArgsConstructor; @@ -81,8 +80,7 @@ public SignUpFinishResponse finishCreateCredential(SignUpFinishRequest request) public ListPasskeysResponse getUserCredentials(UUID userUuid) { List userCredentials = userCredentialRepository.findAllByUserId(userUuid); - List passkeyItems = userCredentialMapper.toDto(userCredentials); - return new ListPasskeysResponse(passkeyItems); + return new ListPasskeysResponse(userCredentialMapper.toDto(userCredentials)); } } From e3165397b8f05a54d9dfe46c44762c5604285940 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Wed, 4 Dec 2024 07:27:30 +0200 Subject: [PATCH 04/11] Add unit tests for UserCredentialManager --- .../service/UserCredentialManagerTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/test/java/com/helioauth/passkeys/api/service/UserCredentialManagerTest.java diff --git a/src/test/java/com/helioauth/passkeys/api/service/UserCredentialManagerTest.java b/src/test/java/com/helioauth/passkeys/api/service/UserCredentialManagerTest.java new file mode 100644 index 0000000..e0e5da7 --- /dev/null +++ b/src/test/java/com/helioauth/passkeys/api/service/UserCredentialManagerTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.helioauth.passkeys.api.service; + +import com.helioauth.passkeys.api.domain.UserCredential; +import com.helioauth.passkeys.api.domain.UserCredentialRepository; +import com.helioauth.passkeys.api.domain.UserRepository; +import com.helioauth.passkeys.api.generated.models.ListPasskeysResponse; +import com.helioauth.passkeys.api.mapper.UserCredentialMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * @author Viktor Stanchev + */ +@ExtendWith(MockitoExtension.class) +class UserCredentialManagerTest { + + @Mock + private UserCredentialRepository userCredentialRepository; + + @Mock + private WebAuthnAuthenticator authenticator; + + @Mock + private UserRepository userRepository; + + @Spy + private UserCredentialMapper userCredentialMapper = Mappers.getMapper(UserCredentialMapper.class); + + @InjectMocks + private UserCredentialManager userCredentialManager; + + @Test + void getUserCredentials_returnsEmpty_whenResultEmpty() { + // Arrange + when(userCredentialRepository.findAllByUserId(any())).thenReturn(Collections.emptyList()); + + // Act + ListPasskeysResponse result = userCredentialManager.getUserCredentials(UUID.randomUUID()); + + // Assert + assertTrue(result.getPasskeys().isEmpty(), "Expected no user credentials"); + } + + @Test + void getUserCredentials_returnsResult_whenResultNotEmpty() { + // Arrange + UUID userUuid = UUID.randomUUID(); + + UserCredential credential = UserCredential.builder() + .id(1L) + .credentialId(UUID.randomUUID().toString()) + .displayName("Credential Name") + .createdAt(Instant.now()) + .lastUsedAt(Instant.now()) + .build(); + + List credentialList = Collections.singletonList(credential); + + when(userCredentialRepository.findAllByUserId(userUuid)).thenReturn(credentialList); + + // Act + ListPasskeysResponse response = userCredentialManager.getUserCredentials(userUuid); + + // Assert + assertNotNull(response); + assertFalse(response.getPasskeys().isEmpty(), "The response should not be empty when credentials exist."); + assertEquals(1, response.getPasskeys().size()); + assertEquals("Credential Name", response.getPasskeys().get(0).getDisplayName()); + } +} \ No newline at end of file From db29f4bb37f8066b93e868bd53f437e8ce5d7090 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Wed, 4 Dec 2024 07:28:27 +0200 Subject: [PATCH 05/11] Add JsonRawValue annotation to SignIn/UpStartResponse schemas --- docs/openapi/components/schemas/SignInStartResponse.yaml | 1 + docs/openapi/components/schemas/SignUpStartResponse.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/openapi/components/schemas/SignInStartResponse.yaml b/docs/openapi/components/schemas/SignInStartResponse.yaml index 12b9384..81862ff 100644 --- a/docs/openapi/components/schemas/SignInStartResponse.yaml +++ b/docs/openapi/components/schemas/SignInStartResponse.yaml @@ -9,3 +9,4 @@ properties: options: type: string description: Options for assertion to pass to `navigator.credentials.create()` + x-field-extra-annotation: '@com.fasterxml.jackson.annotation.JsonRawValue' diff --git a/docs/openapi/components/schemas/SignUpStartResponse.yaml b/docs/openapi/components/schemas/SignUpStartResponse.yaml index b71f9e9..e9144c7 100644 --- a/docs/openapi/components/schemas/SignUpStartResponse.yaml +++ b/docs/openapi/components/schemas/SignUpStartResponse.yaml @@ -9,3 +9,4 @@ properties: options: type: string description: Options to pass to `navigator.credentials.create()` + x-field-extra-annotation: '@com.fasterxml.jackson.annotation.JsonRawValue' From bf8b5fa90d7b7502e807f9d92efc3bbf79b7fb7e Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Wed, 4 Dec 2024 07:29:35 +0200 Subject: [PATCH 06/11] Move credential signup endpoints to UsersController --- .../api/controller/CredentialsController.java | 26 +++++++++---------- .../api/controller/UsersController.java | 16 ++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java b/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java index 4cd97f6..43c6122 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java @@ -17,17 +17,27 @@ package com.helioauth.passkeys.api.controller; import com.fasterxml.jackson.core.JsonProcessingException; -import com.helioauth.passkeys.api.contract.*; +import com.helioauth.passkeys.api.contract.SignInFinishRequest; +import com.helioauth.passkeys.api.contract.SignInFinishResponse; +import com.helioauth.passkeys.api.contract.SignInStartRequest; +import com.helioauth.passkeys.api.contract.SignInStartResponse; +import com.helioauth.passkeys.api.contract.SignUpFinishRequest; +import com.helioauth.passkeys.api.contract.SignUpFinishResponse; +import com.helioauth.passkeys.api.contract.SignUpStartRequest; +import com.helioauth.passkeys.api.contract.SignUpStartResponse; import com.helioauth.passkeys.api.service.UserCredentialManager; import com.helioauth.passkeys.api.service.UserSignInService; import com.helioauth.passkeys.api.service.UserSignupService; - import com.helioauth.passkeys.api.service.exception.SignInFailedException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; import jakarta.validation.Valid; @@ -76,14 +86,4 @@ public SignInFinishResponse finishSignInCredential(@RequestBody SignInFinishRequ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Sign in failed"); } } - - @PostMapping(value = "/credentials/add/start", produces = MediaType.APPLICATION_JSON_VALUE) - public SignUpStartResponse credentialsAddStart(@RequestBody String username) { - return userCredentialManager.createCredential(username); - } - - @PostMapping(value = "/credentials/add/finish", produces = MediaType.APPLICATION_JSON_VALUE) - public SignUpFinishResponse credentialsAddFinish(@RequestBody SignUpFinishRequest request) { - return userCredentialManager.finishCreateCredential(request); - } } \ No newline at end of file diff --git a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java index 848d9fc..6ad1fa5 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java @@ -18,14 +18,20 @@ import com.helioauth.passkeys.api.generated.api.UsersApi; import com.helioauth.passkeys.api.generated.models.ListPasskeysResponse; +import com.helioauth.passkeys.api.generated.models.SignUpFinishRequest; +import com.helioauth.passkeys.api.generated.models.SignUpFinishResponse; +import com.helioauth.passkeys.api.generated.models.SignUpStartResponse; import com.helioauth.passkeys.api.service.UserAccountManager; import com.helioauth.passkeys.api.service.UserCredentialManager; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -52,4 +58,14 @@ public ResponseEntity deleteUser(@PathVariable UUID uuid) { userAccountManager.deleteUser(uuid); return ResponseEntity.noContent().build(); } + + @PostMapping(value = "/credentials/add/start", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity credentialsAddStart(@RequestBody String username) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @PostMapping(value = "/credentials/add/finish", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity credentialsAddFinish(@RequestBody SignUpFinishRequest request) { + throw new UnsupportedOperationException("Not implemented yet"); + } } From a98a3f26f971a181cb3a2c42cb8974b29358a10b Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Thu, 5 Dec 2024 06:44:49 +0200 Subject: [PATCH 07/11] Remove redundant request mapping annotations --- .../ClientApplicationController.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java b/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java index 33cbc92..41565bc 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java @@ -24,13 +24,8 @@ import lombok.RequiredArgsConstructor; import lombok.val; 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.RestController; import java.net.URI; @@ -41,36 +36,27 @@ * @author Viktor Stanchev */ @RestController -@RequestMapping("/admin/v1/apps") @RequiredArgsConstructor public class ClientApplicationController implements ApplicationsApi { private final ClientApplicationService clientApplicationService; - @GetMapping - @Override public ResponseEntity> listAll() { return ResponseEntity.ok(clientApplicationService.listAll()); } - @GetMapping("/{id}") - @Override public ResponseEntity get(@PathVariable UUID id) { return clientApplicationService.get(id) .map(ResponseEntity::ok) .orElseGet(() -> ResponseEntity.notFound().build()); } - @GetMapping("/{id}/api-key") - @Override public ResponseEntity getApiKey(@PathVariable UUID id) { return clientApplicationService.getApiKey(id) .map(ResponseEntity::ok) .orElseGet(() -> ResponseEntity.notFound().build()); } - @PostMapping - @Override public ResponseEntity add(@RequestBody AddApplicationRequest request) { Application created = clientApplicationService.add(request.getName()); @@ -78,10 +64,7 @@ public ResponseEntity add(@RequestBody AddApplicationRequest reques .body(created); } - @PutMapping("/{id}") - @Override public ResponseEntity edit(@PathVariable UUID id, @RequestBody String name) { - val updated = clientApplicationService.edit(id, name); return updated @@ -89,8 +72,6 @@ public ResponseEntity edit(@PathVariable UUID id, @RequestBody Stri .orElseGet(() -> ResponseEntity.notFound().build()); } - @DeleteMapping("/{id}") - @Override public ResponseEntity delete(@PathVariable UUID id) { boolean deleted = clientApplicationService.delete(id); From f2f75b5770dce2f7d3eb35876a61eda4584a02dd Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Thu, 5 Dec 2024 06:47:01 +0200 Subject: [PATCH 08/11] Remove unused import statements and annotations --- .../passkeys/api/controller/UsersController.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java index 6ad1fa5..5b8b201 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java @@ -20,19 +20,15 @@ import com.helioauth.passkeys.api.generated.models.ListPasskeysResponse; import com.helioauth.passkeys.api.generated.models.SignUpFinishRequest; import com.helioauth.passkeys.api.generated.models.SignUpFinishResponse; +import com.helioauth.passkeys.api.generated.models.SignUpStartRequest; import com.helioauth.passkeys.api.generated.models.SignUpStartResponse; import com.helioauth.passkeys.api.service.UserAccountManager; import com.helioauth.passkeys.api.service.UserCredentialManager; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.MediaType; 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.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.UUID; @@ -40,31 +36,26 @@ /** * @author Viktor Stanchev */ -@RestController -@RequestMapping("/v1/users") @Slf4j +@RestController @RequiredArgsConstructor public class UsersController implements UsersApi { private final UserCredentialManager userCredentialManager; private final UserAccountManager userAccountManager; - @GetMapping("/{uuid}/credentials") public ResponseEntity getUserCredentials(@PathVariable UUID uuid) { return ResponseEntity.ok(userCredentialManager.getUserCredentials(uuid)); } - @DeleteMapping("/{uuid}") public ResponseEntity deleteUser(@PathVariable UUID uuid) { userAccountManager.deleteUser(uuid); return ResponseEntity.noContent().build(); } - @PostMapping(value = "/credentials/add/start", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity credentialsAddStart(@RequestBody String username) { throw new UnsupportedOperationException("Not implemented yet"); } - @PostMapping(value = "/credentials/add/finish", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity credentialsAddFinish(@RequestBody SignUpFinishRequest request) { throw new UnsupportedOperationException("Not implemented yet"); } From 380fdac28725ac2f7630f9103cb3b79aff824379 Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Thu, 5 Dec 2024 06:49:03 +0200 Subject: [PATCH 09/11] Changed to generated request and response for adding credentials --- docs/openapi/paths/v1_credentials_add_start.yaml | 2 +- .../passkeys/api/controller/UsersController.java | 6 +++--- .../passkeys/api/mapper/UserCredentialMapper.java | 2 ++ .../passkeys/api/service/UserCredentialManager.java | 12 ++++++------ .../passkeys/api/service/UserSignupService.java | 2 +- .../passkeys/api/service/WebAuthnAuthenticator.java | 2 +- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/openapi/paths/v1_credentials_add_start.yaml b/docs/openapi/paths/v1_credentials_add_start.yaml index 2750bc8..584e9d7 100644 --- a/docs/openapi/paths/v1_credentials_add_start.yaml +++ b/docs/openapi/paths/v1_credentials_add_start.yaml @@ -8,7 +8,7 @@ post: content: application/json: schema: - type: string + $ref: ../components/schemas/SignUpStartRequest.yaml required: true responses: '200': diff --git a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java index 5b8b201..37ec77d 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java @@ -52,11 +52,11 @@ public ResponseEntity deleteUser(@PathVariable UUID uuid) { return ResponseEntity.noContent().build(); } - public ResponseEntity credentialsAddStart(@RequestBody String username) { - throw new UnsupportedOperationException("Not implemented yet"); + public ResponseEntity credentialsAddStart(@RequestBody SignUpStartRequest request) { + return ResponseEntity.ok(userCredentialManager.createCredential(request.getName())); } public ResponseEntity credentialsAddFinish(@RequestBody SignUpFinishRequest request) { - throw new UnsupportedOperationException("Not implemented yet"); + return ResponseEntity.ok(userCredentialManager.finishCreateCredential(request)); } } diff --git a/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java b/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java index 804a882..42e8936 100644 --- a/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java +++ b/src/main/java/com/helioauth/passkeys/api/mapper/UserCredentialMapper.java @@ -38,4 +38,6 @@ public interface UserCredentialMapper { @Mapping(target = "user", ignore = true) @Mapping(target = "id", ignore = true) UserCredential fromCredentialRegistrationResult(CredentialRegistrationResult registrationResultDto); + + com.helioauth.passkeys.api.contract.SignUpStartResponse toLegacySignUpStartResponse(SignUpStartResponse response); } \ No newline at end of file diff --git a/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java b/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java index 4d37649..e8a9d8a 100644 --- a/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java +++ b/src/main/java/com/helioauth/passkeys/api/service/UserCredentialManager.java @@ -17,14 +17,14 @@ package com.helioauth.passkeys.api.service; import com.fasterxml.jackson.core.JsonProcessingException; -import com.helioauth.passkeys.api.contract.SignUpFinishRequest; -import com.helioauth.passkeys.api.contract.SignUpFinishResponse; -import com.helioauth.passkeys.api.contract.SignUpStartResponse; import com.helioauth.passkeys.api.domain.User; import com.helioauth.passkeys.api.domain.UserCredential; import com.helioauth.passkeys.api.domain.UserCredentialRepository; import com.helioauth.passkeys.api.domain.UserRepository; import com.helioauth.passkeys.api.generated.models.ListPasskeysResponse; +import com.helioauth.passkeys.api.generated.models.SignUpFinishRequest; +import com.helioauth.passkeys.api.generated.models.SignUpFinishResponse; +import com.helioauth.passkeys.api.generated.models.SignUpStartResponse; import com.helioauth.passkeys.api.mapper.UserCredentialMapper; import com.helioauth.passkeys.api.service.dto.CredentialRegistrationResult; import com.helioauth.passkeys.api.service.exception.CreateCredentialFailedException; @@ -61,8 +61,8 @@ public SignUpStartResponse createCredential(String name) { public SignUpFinishResponse finishCreateCredential(SignUpFinishRequest request) { try { CredentialRegistrationResult result = webAuthnAuthenticator.finishRegistration( - request.requestId(), - request.publicKeyCredential() + request.getRequestId(), + request.getPublicKeyCredential() ); User user = userRepository.findByName(result.name()).orElseThrow(CreateCredentialFailedException::new); @@ -71,7 +71,7 @@ public SignUpFinishResponse finishCreateCredential(SignUpFinishRequest request) userCredential.setUser(user); userCredentialRepository.save(userCredential); - return new SignUpFinishResponse(request.requestId(), user.getId()); + return new SignUpFinishResponse(request.getRequestId(), user.getId()); } catch (IOException e) { log.error("Register Credential failed", e); throw new SignUpFailedException(); diff --git a/src/main/java/com/helioauth/passkeys/api/service/UserSignupService.java b/src/main/java/com/helioauth/passkeys/api/service/UserSignupService.java index 42e4992..c5420d2 100644 --- a/src/main/java/com/helioauth/passkeys/api/service/UserSignupService.java +++ b/src/main/java/com/helioauth/passkeys/api/service/UserSignupService.java @@ -53,7 +53,7 @@ public SignUpStartResponse startRegistration(String name) { }); try { - return webAuthnAuthenticator.startRegistration(name); + return usercredentialMapper.toLegacySignUpStartResponse(webAuthnAuthenticator.startRegistration(name)); } catch (JsonProcessingException e) { log.error("Register Credential failed", e); throw new SignUpFailedException(); diff --git a/src/main/java/com/helioauth/passkeys/api/service/WebAuthnAuthenticator.java b/src/main/java/com/helioauth/passkeys/api/service/WebAuthnAuthenticator.java index 3aab0e1..a1a6b3e 100644 --- a/src/main/java/com/helioauth/passkeys/api/service/WebAuthnAuthenticator.java +++ b/src/main/java/com/helioauth/passkeys/api/service/WebAuthnAuthenticator.java @@ -21,7 +21,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.helioauth.passkeys.api.config.properties.WebAuthnRelyingPartyProperties; import com.helioauth.passkeys.api.contract.SignInStartResponse; -import com.helioauth.passkeys.api.contract.SignUpStartResponse; +import com.helioauth.passkeys.api.generated.models.SignUpStartResponse; import com.helioauth.passkeys.api.mapper.CredentialRegistrationResultMapper; import com.helioauth.passkeys.api.service.dto.CredentialAssertionResult; import com.helioauth.passkeys.api.service.dto.CredentialRegistrationResult; From 856331089c704b235db81d4f0d8c270d4f9bb2aa Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Thu, 5 Dec 2024 07:10:06 +0200 Subject: [PATCH 10/11] Refactor CrossOrigin annotation in controllers --- .../passkeys/api/controller/CredentialsController.java | 4 +--- .../helioauth/passkeys/api/controller/UsersController.java | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java b/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java index 43c6122..edbf8f7 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/CredentialsController.java @@ -25,7 +25,6 @@ import com.helioauth.passkeys.api.contract.SignUpFinishResponse; import com.helioauth.passkeys.api.contract.SignUpStartRequest; import com.helioauth.passkeys.api.contract.SignUpStartResponse; -import com.helioauth.passkeys.api.service.UserCredentialManager; import com.helioauth.passkeys.api.service.UserSignInService; import com.helioauth.passkeys.api.service.UserSignupService; import com.helioauth.passkeys.api.service.exception.SignInFailedException; @@ -45,16 +44,15 @@ /** * @author Viktor Stanchev */ +@Slf4j @RestController @RequestMapping("/v1") @CrossOrigin(origins = "*") -@Slf4j @RequiredArgsConstructor public class CredentialsController { private final UserSignInService userSignInService; private final UserSignupService userSignupService; - private final UserCredentialManager userCredentialManager; @PostMapping(value = "/signup/start", produces = MediaType.APPLICATION_JSON_VALUE) public SignUpStartResponse postSignupStart(@RequestBody @Valid SignUpStartRequest request) { diff --git a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java index 37ec77d..30b2ada 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/UsersController.java @@ -27,6 +27,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -52,10 +53,12 @@ public ResponseEntity deleteUser(@PathVariable UUID uuid) { return ResponseEntity.noContent().build(); } + @CrossOrigin(origins = "*") public ResponseEntity credentialsAddStart(@RequestBody SignUpStartRequest request) { return ResponseEntity.ok(userCredentialManager.createCredential(request.getName())); } + @CrossOrigin(origins = "*") public ResponseEntity credentialsAddFinish(@RequestBody SignUpFinishRequest request) { return ResponseEntity.ok(userCredentialManager.finishCreateCredential(request)); } From 093c574af27da778e54fe23c97a88efde3dfe8dc Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Thu, 5 Dec 2024 07:20:02 +0200 Subject: [PATCH 11/11] Remove dependency graph update step from Maven workflow --- .github/workflows/maven.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 667784f..ab6c3f8 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -28,8 +28,4 @@ jobs: distribution: 'temurin' cache: maven - name: Verify with Maven - run: mvn -B -DskipTests verify --file pom.xml - - # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - - name: Update dependency graph - uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 + run: mvn -B -DskipTests verify --file pom.xml \ No newline at end of file