Compare commits

..

2 Commits

Author SHA1 Message Date
7a2a366dd5 SpringBoot introduce GameProposalController 2023-09-03 20:38:40 +02:00
36edc91cf3 SpringBoot: GameProposal
- rest controller
- poposal endpoint
2023-09-03 16:27:54 +02:00
10 changed files with 175 additions and 101 deletions

View File

@ -1,43 +0,0 @@
package djmil.cordacheckers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import djmil.cordacheckers.cordaclient.CordaClient;
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
@RestController
public class ApiController {
@Autowired
CordaClient cordaClient;
@GetMapping("/api/badjokes")
public ResponseEntity<Joke> badJokes() {
List<VirtualNode> vNodeList = cordaClient.getVirtualNodeList();
Joke joke = new Joke("What do you call a fly without wings? A walk! " + vNodeList.get(1).holdingIdentity().x500Name());
return ResponseEntity.ok(joke);
}
/**
*
* @return a Json list of active games
*/
@GetMapping("/api/gameproposals")
public ResponseEntity<String> dashboard(@AuthenticationPrincipal ApiUserDetails user) {
System.out.println("List of active games for "
+ "user: " + user.getUsername()
+ " with HoldingIdentity ShortHash: " + user.getHoldingIdentity().shortHash());
return ResponseEntity.ok("{ \"UnconsumedGameProposals\" : [\"id_game1\", \"id_game2\"] }" );
}
}

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,6 +17,7 @@ import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
import djmil.cordacheckers.cordaclient.dao.Color;
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
import djmil.cordacheckers.cordaclient.dao.VirtualNodeList;
import djmil.cordacheckers.cordaclient.dao.flow.RequestBody;
@ -72,6 +73,15 @@ public class CordaClient {
return gameProposalsJsonString;
}
public String sendGameProposal(
HoldingIdentity sender,
HoldingIdentity receiver,
Color receiverColor,
String message
) {
return "";
}
private String cordaFlowExecute(HoldingIdentity holdingIdentity, RequestBody requestBody) {
try {

View File

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

View File

@ -0,0 +1,13 @@
package djmil.cordacheckers.cordaclient.dao;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonDeserialize
public record GameProposal(
String sender,
String recipient,
Color recipientColor,
String message,
String id) {
}

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

@ -0,0 +1,79 @@
package djmil.cordacheckers.gameproposal;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import djmil.cordacheckers.cordaclient.CordaClient;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
import djmil.cordacheckers.cordaclient.dao.Color;
import djmil.cordacheckers.user.HoldingIdentityResolver;
import djmil.cordacheckers.user.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
@RequestMapping("gameproposal")
public class GameProposalController {
@Autowired
CordaClient cordaClient;
@Autowired
HoldingIdentityResolver holdingIdentityResolver;
@GetMapping
public ResponseEntity<String> findAllUnconsumed(
@AuthenticationPrincipal User player
) {
String gpList = cordaClient.listGameProposals(player.getHoldingIdentity());
return ResponseEntity.ok(gpList);
}
// @PostMapping()
// public ResponseEntity<String> gameproposalSend(@AuthenticationPrincipal ApiUserDetails user) {
// return ResponseEntity.ok("");
// }
@PostMapping()
public ResponseEntity<Void> createGameProposal(
@AuthenticationPrincipal User sender,
@RequestBody GameProposal gpRequest,
UriComponentsBuilder ucb
) {
//sender.get
final HoldingIdentity gpSender = sender.getHoldingIdentity();
// TODO: throw execption with custom type
final HoldingIdentity gpReceiver = holdingIdentityResolver.getByUsername(gpRequest.recipient());
final Color gpReceiverColor = gpRequest.recipientColor();
String newGameProposalUuid = cordaClient.sendGameProposal(
gpSender,
gpReceiver,
gpReceiverColor,
gpRequest.message()
);
URI locationOfNewGameProposal = ucb
.path("gameproposal/{id}")
.buildAndExpand(newGameProposalUuid)
.toUri();
return ResponseEntity
.created(locationOfNewGameProposal)
.build();
}
}

View File

@ -1,4 +1,4 @@
package djmil.cordacheckers.cordaclient;
package djmil.cordacheckers.user;
import java.util.HashMap;
import java.util.List;
@ -9,6 +9,7 @@ import javax.naming.InvalidNameException;
import org.springframework.stereotype.Service;
import djmil.cordacheckers.cordaclient.CordaClient;
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
@ -18,10 +19,10 @@ public class HoldingIdentityResolver {
/*
* 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)
* Ideally, we want to be able to force update cache, had the corda
* 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) {
this.cache = setCache(cordaClient);
@ -37,27 +38,29 @@ public class HoldingIdentityResolver {
var identity = vNode.holdingIdentity();
if (identity.isPlayer()) {
map.put(identity.getName().toLowerCase(locale), identity);
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();
System.out.println("Unable to get ShorHash list of Corda VirtualNodes: "+e.getMessage());
}
return map;
}
/*
* @param apiUserName
* @param playerName
* 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));
public HoldingIdentity getByUsername(String userName) {
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 djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
public class ApiUserDetails extends User {
public class User extends org.springframework.security.core.userdetails.User {
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());
this.holdingIdentity = holdingIdentity;
@ -17,5 +16,4 @@ public class ApiUserDetails extends User {
public HoldingIdentity getHoldingIdentity() {
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);
}
}