SpringBoot introduce GameProposalController

This commit is contained in:
djmil 2023-09-03 20:38:40 +02:00
parent 36edc91cf3
commit 7a2a366dd5
11 changed files with 97 additions and 91 deletions

View File

@ -1,43 +0,0 @@
package djmil.cordacheckers;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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 HoldingIdentityResolver holdingIdentityResolver;
public ApiUserDetailsService(
PasswordEncoder encoder,
HoldingIdentityResolver holdingIdentityNameResolver) {
this.encoder = encoder;
this.holdingIdentityResolver = holdingIdentityNameResolver;
}
@Override
public ApiUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
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);
User.UserBuilder userBuilder = User.builder();
UserDetails user = userBuilder
.username(username)
.password(encoder.encode("qaz123"))
.build();
return new ApiUserDetails(user, holdingIdentity);
}
}

View File

@ -17,12 +17,12 @@ import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
import djmil.cordacheckers.cordaclient.dao.Color;
import djmil.cordacheckers.cordaclient.dao.VirtualNode; import djmil.cordacheckers.cordaclient.dao.VirtualNode;
import djmil.cordacheckers.cordaclient.dao.VirtualNodeList; import djmil.cordacheckers.cordaclient.dao.VirtualNodeList;
import djmil.cordacheckers.cordaclient.dao.flow.RequestBody; import djmil.cordacheckers.cordaclient.dao.flow.RequestBody;
import djmil.cordacheckers.cordaclient.dao.flow.ResponseBody; import djmil.cordacheckers.cordaclient.dao.flow.ResponseBody;
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty; import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
import djmil.cordacheckers.game.PlayerColor;
@Service @Service
public class CordaClient { public class CordaClient {
@ -73,7 +73,12 @@ public class CordaClient {
return gameProposalsJsonString; return gameProposalsJsonString;
} }
public String sendGameProposal(HoldingIdentity sender, HoldingIdentity receiver, PlayerColor receiverColor, String message) { public String sendGameProposal(
HoldingIdentity sender,
HoldingIdentity receiver,
Color receiverColor,
String message
) {
return ""; return "";
} }

View File

@ -0,0 +1,6 @@
package djmil.cordacheckers.cordaclient.dao;
public enum Color {
WHITE,
BLACK
}

View File

@ -6,8 +6,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
public record GameProposal( public record GameProposal(
String sender, String sender,
String recipient, String recipient,
String youPlayAs, Color recipientColor,
String additionalMessage, String message,
String id) { String id) {
} }

View File

@ -1,6 +0,0 @@
package djmil.cordacheckers.game;
public enum PlayerColor {
WHITE,
BLACK
}

View File

@ -1,9 +0,0 @@
package djmil.cordacheckers.gameproposal;
public record CreateGameProposal(
String opponentName,
String opponentColor,
String message)
{
}

View File

@ -0,0 +1,12 @@
package djmil.cordacheckers.gameproposal;
import djmil.cordacheckers.cordaclient.dao.Color;
public record GameProposal(
String sender,
String recipient,
Color recipientColor,
String message)
{
}

View File

@ -1,4 +1,4 @@
package djmil.cordacheckers; package djmil.cordacheckers.gameproposal;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
@ -11,11 +11,10 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import djmil.cordacheckers.cordaclient.CordaClient; import djmil.cordacheckers.cordaclient.CordaClient;
import djmil.cordacheckers.cordaclient.HoldingIdentityResolver;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
import djmil.cordacheckers.cordaclient.dao.VirtualNode; import djmil.cordacheckers.cordaclient.dao.Color;
import djmil.cordacheckers.game.PlayerColor; import djmil.cordacheckers.user.HoldingIdentityResolver;
import djmil.cordacheckers.gameproposal.CreateGameProposal; import djmil.cordacheckers.user.User;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@ -23,7 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
@RestController @RestController
@RequestMapping("/gameproposal") @RequestMapping("gameproposal")
public class GameProposalController { public class GameProposalController {
@Autowired @Autowired
@ -34,9 +33,9 @@ public class GameProposalController {
@GetMapping @GetMapping
public ResponseEntity<String> findAllUnconsumed( public ResponseEntity<String> findAllUnconsumed(
@AuthenticationPrincipal ApiUserDetails user @AuthenticationPrincipal User player
) { ) {
String gpList = cordaClient.listGameProposals(user.getHoldingIdentity()); String gpList = cordaClient.listGameProposals(player.getHoldingIdentity());
return ResponseEntity.ok(gpList); return ResponseEntity.ok(gpList);
} }
@ -48,15 +47,17 @@ public class GameProposalController {
// } // }
@PostMapping() @PostMapping()
public ResponseEntity<Void> create( public ResponseEntity<Void> createGameProposal(
@AuthenticationPrincipal ApiUserDetails player, @AuthenticationPrincipal User sender,
@RequestBody CreateGameProposal gpRequest, @RequestBody GameProposal gpRequest,
UriComponentsBuilder ucb UriComponentsBuilder ucb
) { ) {
final HoldingIdentity gpSender = player.getHoldingIdentity();
//sender.get
final HoldingIdentity gpSender = sender.getHoldingIdentity();
// TODO: throw execption with custom type // TODO: throw execption with custom type
final HoldingIdentity gpReceiver = holdingIdentityResolver.getByCommonName(gpRequest.opponentName()); final HoldingIdentity gpReceiver = holdingIdentityResolver.getByUsername(gpRequest.recipient());
final PlayerColor gpReceiverColor = PlayerColor.valueOf(gpRequest.opponentColor()); final Color gpReceiverColor = gpRequest.recipientColor();
String newGameProposalUuid = cordaClient.sendGameProposal( String newGameProposalUuid = cordaClient.sendGameProposal(
gpSender, gpSender,

View File

@ -1,4 +1,4 @@
package djmil.cordacheckers.cordaclient; package djmil.cordacheckers.user;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -9,6 +9,7 @@ import javax.naming.InvalidNameException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import djmil.cordacheckers.cordaclient.CordaClient;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
import djmil.cordacheckers.cordaclient.dao.VirtualNode; import djmil.cordacheckers.cordaclient.dao.VirtualNode;
@ -18,10 +19,10 @@ public class HoldingIdentityResolver {
/* /*
* NOTE: Proof-of-Concept impementation * NOTE: Proof-of-Concept impementation
* Ideally, we want to be able to update HoldingIdentity cache, had the * Ideally, we want to be able to force update cache, had the corda
* corda cluster configuration changed (aka in case of a cache miss) * cluster configuration changed (aka in case of a cache miss)
*/ */
final Map<String, HoldingIdentity> cache; final Map<String, HoldingIdentity> cache; // PlayerName to ShortHash
HoldingIdentityResolver(CordaClient cordaClient) { HoldingIdentityResolver(CordaClient cordaClient) {
this.cache = setCache(cordaClient); this.cache = setCache(cordaClient);
@ -37,27 +38,29 @@ public class HoldingIdentityResolver {
var identity = vNode.holdingIdentity(); var identity = vNode.holdingIdentity();
if (identity.isPlayer()) { if (identity.isPlayer()) {
map.put(identity.getName().toLowerCase(locale), identity); map.put(
identity.getName().toLowerCase(locale),
identity
);
} }
} }
} catch (InvalidNameException e) { } catch (InvalidNameException e) {
// TODO: logs // TODO: logs
System.out.println("Unable to get ShorHash map for Corda virtual nodes: "+e.getExplanation()); System.out.println("Unable to get ShorHash list of Corda VirtualNodes: "+e.getMessage());
e.printStackTrace();
} }
return map; return map;
} }
/* /*
* @param apiUserName * @param playerName
* HoldingIdentity x500 name typically looks like * HoldingIdentity x500 name typically looks like
* "CN=Bob, OU=Player, O=Checkers, L=Kviv, C=UA" * "CN=Bob, OU=Player, O=Checkers, L=Kviv, C=UA"
* CN - is a common name, expected to be unique for CordaCheckers setup. * CN - is a common name, expected to be unique for CordaCheckers setup.
* *
* @return HoldingIdentity * @return HoldingIdentity
*/ */
public HoldingIdentity getByCommonName(String apiUserName) { public HoldingIdentity getByUsername(String userName) {
return this.cache.get(apiUserName.toLowerCase(locale)); return this.cache.get(userName.toLowerCase(locale));
} }
} }

View File

@ -1,14 +1,13 @@
package djmil.cordacheckers; package djmil.cordacheckers.user;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity; import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
public class ApiUserDetails extends User { public class User extends org.springframework.security.core.userdetails.User {
private final HoldingIdentity holdingIdentity; private final HoldingIdentity holdingIdentity;
public ApiUserDetails(UserDetails user, HoldingIdentity holdingIdentity) { public User(UserDetails user, HoldingIdentity holdingIdentity) {
super(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities()); super(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
this.holdingIdentity = holdingIdentity; this.holdingIdentity = holdingIdentity;
@ -17,5 +16,4 @@ public class ApiUserDetails extends User {
public HoldingIdentity getHoldingIdentity() { public HoldingIdentity getHoldingIdentity() {
return this.holdingIdentity; return this.holdingIdentity;
} }
} }

View File

@ -0,0 +1,39 @@
package djmil.cordacheckers.user;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
private final PasswordEncoder encoder;
private final HoldingIdentityResolver holdingIdentityResolver;
public UserService(
PasswordEncoder encoder,
HoldingIdentityResolver holdingIdentityResolver) {
this.encoder = encoder;
this.holdingIdentityResolver = holdingIdentityResolver;
}
@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
final var holdingIdentity = this.holdingIdentityResolver.getByUsername(username);
if (holdingIdentity == null) {
throw new UsernameNotFoundException("Can't find HoldingIdentity for the user '"+username+ "'");
}
System.out.println("Load user "+username);
org.springframework.security.core.userdetails.User.UserBuilder userBuilder = org.springframework.security.core.userdetails.User.builder();
UserDetails baseUser = userBuilder
.username(username)
.password(this.encoder.encode("qaz123"))
.build();
return new User(baseUser, holdingIdentity);
}
}