From 806c61958806851be27222228deda5559955b048 Mon Sep 17 00:00:00 2001 From: djmil Date: Thu, 31 Aug 2023 15:47:00 +0200 Subject: [PATCH] SpringBoot: HoldingIdentity DAO object --- .../djmil/cordacheckers/ApiController.java | 14 ++--- .../djmil/cordacheckers/ApiUserDetails.java | 14 +++-- .../cordacheckers/ApiUserDetailsService.java | 18 +++--- .../HoldingIdentityNameResolver.java | 51 --------------- .../cordaclient/CordaClient.java | 29 ++++++--- .../cordaclient/HoldingIdentityResolver.java | 63 +++++++++++++++++++ .../HoldingIdentity.java} | 8 ++- .../cordaclient/dao/VirtualNode.java | 6 ++ .../VirtualNodes.java} | 4 +- .../cordaclient/pojo/virtualNodes.java | 6 -- .../djmil/cordacheckers/CordaClientTest.java | 10 +-- .../CordacheckersApplicationTests.java | 12 ++-- .../cordacheckers/HoldingIdentityTest.java | 6 +- 13 files changed, 137 insertions(+), 104 deletions(-) delete mode 100644 backend/src/main/java/djmil/cordacheckers/HoldingIdentityNameResolver.java create mode 100644 backend/src/main/java/djmil/cordacheckers/cordaclient/HoldingIdentityResolver.java rename backend/src/main/java/djmil/cordacheckers/cordaclient/{pojo/holdingIdentity.java => dao/HoldingIdentity.java} (77%) create mode 100644 backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNode.java rename backend/src/main/java/djmil/cordacheckers/cordaclient/{pojo/virtualnode.java => dao/VirtualNodes.java} (54%) delete mode 100644 backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualNodes.java diff --git a/backend/src/main/java/djmil/cordacheckers/ApiController.java b/backend/src/main/java/djmil/cordacheckers/ApiController.java index 2a67e7f..0a80227 100644 --- a/backend/src/main/java/djmil/cordacheckers/ApiController.java +++ b/backend/src/main/java/djmil/cordacheckers/ApiController.java @@ -9,20 +9,20 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import djmil.cordacheckers.cordaclient.CordaClient; -import djmil.cordacheckers.cordaclient.pojo.virtualNodes; +import djmil.cordacheckers.cordaclient.dao.VirtualNode; @RestController public class ApiController { @Autowired - CordaClient cordaclient; + CordaClient cordaClient; @GetMapping("/api/badjokes") public ResponseEntity badJokes() { - List vNodes = cordaclient.getVirtualnode(); + List vNodeList = cordaClient.getVirtualNodeList(); - Joke joke = new Joke("What do you call a fly without wings? A walk! " + vNodes.get(1).holdingIdentity().x500Name()); + Joke joke = new Joke("What do you call a fly without wings? A walk! " + vNodeList.get(1).holdingIdentity().x500Name()); return ResponseEntity.ok(joke); } @@ -35,9 +35,9 @@ public class ApiController { public ResponseEntity dashboard(@AuthenticationPrincipal ApiUserDetails user) { System.out.println("List of active games for " + "user: " + user.getUsername() - + " with shortIdentityHash: " + user.getHoldingIdentityShortHash()); - - return ResponseEntity.ok("{ \"ActiveGames\" : [\"id_game1\", \"id_game2\"] }" ); + + " with HoldingIdentity ShortHash: " + user.getHoldingIdentity().shortHash()); + + return ResponseEntity.ok("{ \"UnconsumedGameProposals\" : [\"id_game1\", \"id_game2\"] }" ); } } \ No newline at end of file diff --git a/backend/src/main/java/djmil/cordacheckers/ApiUserDetails.java b/backend/src/main/java/djmil/cordacheckers/ApiUserDetails.java index 6a6034c..8e701eb 100644 --- a/backend/src/main/java/djmil/cordacheckers/ApiUserDetails.java +++ b/backend/src/main/java/djmil/cordacheckers/ApiUserDetails.java @@ -3,17 +3,19 @@ package djmil.cordacheckers; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; -public class ApiUserDetails extends User { - private final String holdingIdentityShortHash; +import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; - public ApiUserDetails(UserDetails user, String holdingIdentityShortHash) { +public class ApiUserDetails extends User { + private final HoldingIdentity holdingIdentity; + + public ApiUserDetails(UserDetails user, HoldingIdentity holdingIdentity) { super(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities()); - this.holdingIdentityShortHash = holdingIdentityShortHash; + this.holdingIdentity = holdingIdentity; } - public String getHoldingIdentityShortHash() { - return this.holdingIdentityShortHash; + public HoldingIdentity getHoldingIdentity() { + return this.holdingIdentity; } } \ No newline at end of file diff --git a/backend/src/main/java/djmil/cordacheckers/ApiUserDetailsService.java b/backend/src/main/java/djmil/cordacheckers/ApiUserDetailsService.java index f333d6e..a5af87a 100644 --- a/backend/src/main/java/djmil/cordacheckers/ApiUserDetailsService.java +++ b/backend/src/main/java/djmil/cordacheckers/ApiUserDetailsService.java @@ -7,25 +7,27 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import djmil.cordacheckers.cordaclient.HoldingIdentityResolver; +import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; + @Service public class ApiUserDetailsService implements UserDetailsService { private final PasswordEncoder encoder; - private final HoldingIdentityNameResolver holdingIdentityNameResolver; + private final HoldingIdentityResolver holdingIdentityResolver; public ApiUserDetailsService( PasswordEncoder encoder, - HoldingIdentityNameResolver holdingIdentityNameResolver) { + HoldingIdentityResolver holdingIdentityNameResolver) { this.encoder = encoder; - this.holdingIdentityNameResolver = holdingIdentityNameResolver; + this.holdingIdentityResolver = holdingIdentityNameResolver; } @Override public ApiUserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - String shortHash = holdingIdentityNameResolver.getShortHashBy(username); - if (shortHash == null) { - throw new UsernameNotFoundException("ShortHash for user '" - +username+ "' not found"); + HoldingIdentity holdingIdentity = holdingIdentityResolver.getByCommonName(username); + if (holdingIdentity == null) { + throw new UsernameNotFoundException("Can't find HoldingIdentity for the user '"+username+ "'"); } System.out.println("Load user "+username); @@ -36,6 +38,6 @@ public class ApiUserDetailsService implements UserDetailsService { .password(encoder.encode("qaz123")) .build(); - return new ApiUserDetails(user, shortHash); + return new ApiUserDetails(user, holdingIdentity); } } diff --git a/backend/src/main/java/djmil/cordacheckers/HoldingIdentityNameResolver.java b/backend/src/main/java/djmil/cordacheckers/HoldingIdentityNameResolver.java deleted file mode 100644 index 0cc2843..0000000 --- a/backend/src/main/java/djmil/cordacheckers/HoldingIdentityNameResolver.java +++ /dev/null @@ -1,51 +0,0 @@ -package djmil.cordacheckers; - -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.naming.InvalidNameException; - -import org.springframework.stereotype.Service; - -import djmil.cordacheckers.cordaclient.CordaClient; -import djmil.cordacheckers.cordaclient.pojo.virtualNodes; - -@Service -public class HoldingIdentityNameResolver { - static final Locale locale = Locale.getDefault(); - - Map cnName2shortHash; - - HoldingIdentityNameResolver(CordaClient client) { - this.cnName2shortHash = setCnName2shortHash(client); - } - - private static Map setCnName2shortHash(CordaClient client) { - Map map = new HashMap<>(); - - List vNodesList = client.getVirtualnode(); - - try { - for (virtualNodes vNode : vNodesList) { - var identity = vNode.holdingIdentity(); - - if (identity.isPlayer()) { - map.put(identity.getName().toLowerCase(locale), identity.shortHash()); - } - } - } catch (InvalidNameException e) { - // TODO: logs - System.out.println("Unable to get ShorHash map for Corda virtual nodes: "+e.getExplanation()); - e.printStackTrace(); - } - - System.out.println("ApiUserShortHashMap " + map); - return map; - } - - String getShortHashBy(String apiUserName) { - return this.cnName2shortHash.get(apiUserName.toLowerCase(locale)); - } -} diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java index e32574b..b7d110e 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java @@ -10,8 +10,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import djmil.cordacheckers.cordaclient.pojo.virtualNodes; -import djmil.cordacheckers.cordaclient.pojo.virtualnode; +import djmil.cordacheckers.cordaclient.dao.VirtualNode; +import djmil.cordacheckers.cordaclient.dao.VirtualNodes; @Service public class CordaClient { @@ -25,20 +25,19 @@ public class CordaClient { this.restTemplate = restTemplate; } - public List getVirtualnode() { + public List getVirtualNodeList() { // Request authorization header - HttpHeaders headers = new HttpHeaders(); - headers.setBasicAuth("admin", "admin"); + HttpHeaders headers = basicAuthorizationHeader(); // Request final HttpEntity request = new HttpEntity<>(headers); - ResponseEntity resp = this.restTemplate.exchange( + ResponseEntity resp = this.restTemplate.exchange( "https://localhost:8888/api/v1/virtualnode", HttpMethod.GET, request, - virtualnode.class ); + VirtualNodes.class ); // TODO: throw exeption instead if (resp.getStatusCode() != HttpStatus.OK || !resp.hasBody()) { @@ -47,4 +46,20 @@ public class CordaClient { return resp.getBody().virtualNodes(); } + + // public String getGemeProposals(String ) { + // // Request authorization header + // HttpHeaders headers = basicAuthorizationHeader(); + + // // Request + // final HttpEntity request = new HttpEntity<>(headers); + + // } + + private HttpHeaders basicAuthorizationHeader() { + HttpHeaders headers = new HttpHeaders(); + headers.setBasicAuth("admin", "admin"); + + return headers; + } } diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/HoldingIdentityResolver.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/HoldingIdentityResolver.java new file mode 100644 index 0000000..fd9b5d3 --- /dev/null +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/HoldingIdentityResolver.java @@ -0,0 +1,63 @@ +package djmil.cordacheckers.cordaclient; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.naming.InvalidNameException; + +import org.springframework.stereotype.Service; + +import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; +import djmil.cordacheckers.cordaclient.dao.VirtualNode; + +@Service +public class HoldingIdentityResolver { + static final Locale locale = Locale.getDefault(); + + /* + * NOTE: Proof-of-Concept impementation + * Ideally, we want to be able to update HoldingIdentity cache, had the + * corda cluster configuration changed (aka in case of a cache miss) + */ + final Map cache; + + HoldingIdentityResolver(CordaClient cordaClient) { + this.cache = setCache(cordaClient); + } + + private static Map setCache(CordaClient cordaClient) { + Map map = new HashMap<>(); + + List vNodeList = cordaClient.getVirtualNodeList(); + + try { + for (VirtualNode vNode : vNodeList) { + var identity = vNode.holdingIdentity(); + + if (identity.isPlayer()) { + map.put(identity.getName().toLowerCase(locale), identity); + } + } + } catch (InvalidNameException e) { + // TODO: logs + System.out.println("Unable to get ShorHash map for Corda virtual nodes: "+e.getExplanation()); + e.printStackTrace(); + } + + return map; + } + + /* + * @param apiUserName + * HoldingIdentity x500 name typically looks like + * "CN=Bob, OU=Player, O=Checkers, L=Kviv, C=UA" + * CN - is a common name, expected to be unique for CordaCheckers setup. + * + * @return HoldingIdentity + */ + public HoldingIdentity getByCommonName(String apiUserName) { + return this.cache.get(apiUserName.toLowerCase(locale)); + } +} diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/holdingIdentity.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/HoldingIdentity.java similarity index 77% rename from backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/holdingIdentity.java rename to backend/src/main/java/djmil/cordacheckers/cordaclient/dao/HoldingIdentity.java index 37cb3c1..b0f3539 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/holdingIdentity.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/HoldingIdentity.java @@ -1,4 +1,6 @@ -package djmil.cordacheckers.cordaclient.pojo; +package djmil.cordacheckers.cordaclient.dao; + +import java.io.Serializable; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; @@ -7,7 +9,7 @@ import javax.naming.ldap.Rdn; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) -public record holdingIdentity(String x500Name, String shortHash) { +public record HoldingIdentity(String x500Name, String shortHash) implements Serializable { public String getName() throws InvalidNameException { LdapName ln = new LdapName(x500Name); @@ -18,7 +20,7 @@ public record holdingIdentity(String x500Name, String shortHash) { } } - throw new IllegalArgumentException("CN was not found"); + throw new IllegalArgumentException("CN not found: "+ x500Name); } public boolean isPlayer() throws InvalidNameException { diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNode.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNode.java new file mode 100644 index 0000000..1acc5b4 --- /dev/null +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNode.java @@ -0,0 +1,6 @@ +package djmil.cordacheckers.cordaclient.dao; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record VirtualNode(HoldingIdentity holdingIdentity) { } diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualnode.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNodes.java similarity index 54% rename from backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualnode.java rename to backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNodes.java index 6b56cb7..538a789 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualnode.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/VirtualNodes.java @@ -1,8 +1,8 @@ -package djmil.cordacheckers.cordaclient.pojo; +package djmil.cordacheckers.cordaclient.dao; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) -public record virtualnode(List virtualNodes) { } +public record VirtualNodes(List virtualNodes) { } diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualNodes.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualNodes.java deleted file mode 100644 index 39f9368..0000000 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/pojo/virtualNodes.java +++ /dev/null @@ -1,6 +0,0 @@ -package djmil.cordacheckers.cordaclient.pojo; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record virtualNodes(holdingIdentity holdingIdentity) { } diff --git a/backend/src/test/java/djmil/cordacheckers/CordaClientTest.java b/backend/src/test/java/djmil/cordacheckers/CordaClientTest.java index 4821dc8..99bbf23 100644 --- a/backend/src/test/java/djmil/cordacheckers/CordaClientTest.java +++ b/backend/src/test/java/djmil/cordacheckers/CordaClientTest.java @@ -6,8 +6,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import djmil.cordacheckers.cordaclient.CordaClient; -import djmil.cordacheckers.cordaclient.pojo.holdingIdentity; -import djmil.cordacheckers.cordaclient.pojo.virtualNodes; +import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; +import djmil.cordacheckers.cordaclient.dao.VirtualNode; import static org.assertj.core.api.Assertions.assertThat; @@ -24,10 +24,10 @@ public class CordaClientTest { @Test void whenGetVirtualnode_thenListHoldingIdentity() throws GeneralSecurityException, IOException, InvalidNameException { - List vNodes = cordaclient.getVirtualnode(); + List vNodes = cordaclient.getVirtualNodeList(); - holdingIdentity identity = vNodes.get(0).holdingIdentity(); - assertThat(identity.getName()).isEqualTo("NotaryRep1"); + HoldingIdentity identity = vNodes.get(0).holdingIdentity(); + assertThat(identity.getName()).isEqualTo("Bob"); } } diff --git a/backend/src/test/java/djmil/cordacheckers/CordacheckersApplicationTests.java b/backend/src/test/java/djmil/cordacheckers/CordacheckersApplicationTests.java index 6a46fa7..2d1ec5f 100644 --- a/backend/src/test/java/djmil/cordacheckers/CordacheckersApplicationTests.java +++ b/backend/src/test/java/djmil/cordacheckers/CordacheckersApplicationTests.java @@ -27,8 +27,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; -import djmil.cordacheckers.cordaclient.pojo.holdingIdentity; -import djmil.cordacheckers.cordaclient.pojo.virtualnode; +import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; +import djmil.cordacheckers.cordaclient.dao.VirtualNodes; import static org.assertj.core.api.Assertions.assertThat; @@ -80,19 +80,19 @@ class CordacheckersApplicationTests { // Request final HttpEntity request = new HttpEntity<>(headers); - final ResponseEntity response = new RestTemplate(requestFactory) - .exchange("https://localhost:8888/api/v1/virtualnode", HttpMethod.GET, request, virtualnode.class); + final ResponseEntity response = new RestTemplate(requestFactory) + .exchange("https://localhost:8888/api/v1/virtualnode", HttpMethod.GET, request, VirtualNodes.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(response.hasBody()); - virtualnode vNode = response.getBody(); + VirtualNodes vNode = response.getBody(); assertThat(vNode).isNotNull(); if (vNode != null) { assertThat(vNode.virtualNodes().size() == 5); } - holdingIdentity identity = vNode.virtualNodes().get(0).holdingIdentity(); + HoldingIdentity identity = vNode.virtualNodes().get(0).holdingIdentity(); assertThat(identity.x500Name().contains("NotaryRep1")); } } diff --git a/backend/src/test/java/djmil/cordacheckers/HoldingIdentityTest.java b/backend/src/test/java/djmil/cordacheckers/HoldingIdentityTest.java index 2838094..5bda5b1 100644 --- a/backend/src/test/java/djmil/cordacheckers/HoldingIdentityTest.java +++ b/backend/src/test/java/djmil/cordacheckers/HoldingIdentityTest.java @@ -2,7 +2,7 @@ package djmil.cordacheckers; import org.junit.jupiter.api.Test; -import djmil.cordacheckers.cordaclient.pojo.holdingIdentity; +import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; import static org.assertj.core.api.Assertions.assertThat; @@ -14,8 +14,8 @@ public class HoldingIdentityTest { @Test public void isPlayerTest() throws InvalidNameException { - holdingIdentity alice = new holdingIdentity("CN=Alice, OU=Player, O=Checkers, L=Zug, C=CH", "HHHDDD"); - holdingIdentity bob = new holdingIdentity("CN=Bob, OU=Other, O=Checkers, L=Zug, C=CH", "HHHDDD"); + HoldingIdentity alice = new HoldingIdentity("CN=Alice, OU=Player, O=Checkers, L=Zug, C=CH", "HHHDDD"); + HoldingIdentity bob = new HoldingIdentity("CN=Bob, OU=Other, O=Checkers, L=Zug, C=CH", "HHHDDD"); assertThat(alice.getName()).isEqualTo("Alice"); assertThat(alice.isPlayer()).isEqualTo(true);