Skip to content

Commit

Permalink
endpoints: add authentication protection on scoreboards
Browse files Browse the repository at this point in the history
  • Loading branch information
PoustouFlan committed Aug 1, 2024
1 parent d4e7c18 commit 026b7c2
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 87 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.cryptodrink.converter;

import io.vertx.ext.auth.User;
import org.cryptodrink.data.model.ScoreboardModel;
import org.cryptodrink.data.repository.UserRepository;
import org.cryptodrink.domain.entity.ScoreboardEntity;
import org.cryptodrink.domain.entity.UserEntity;
import org.cryptodrink.presentation.rest.response.ScoreboardResponse;
Expand All @@ -21,6 +23,8 @@ public ScoreboardEntity convert(ScoreboardModel scoreboard)
return new ScoreboardEntity(
scoreboard.getId(),
scoreboard.getName(),
userConverter.convert(scoreboard.getOwner()),
scoreboard.getIsPublic(),
scoreboard.getUsers().stream().map(userConverter::convert).toList()
);
}
Expand All @@ -29,6 +33,8 @@ public ScoreboardResponse convert(ScoreboardEntity scoreboard)
{
return new ScoreboardResponse(
scoreboard.getName(),
scoreboard.getOwner().getUsername(),
scoreboard.getIsPublic(),
scoreboard.getUsers().stream().map(UserEntity::getUsername).toList()
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/cryptodrink/data/model/ScoreboardModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public class ScoreboardModel {
private Long id;
private String name;

@OneToOne
private UserModel owner;
private Boolean isPublic;

@ManyToMany
@JoinTable(
name = "scoreboard_users",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
public class ScoreboardEntity {
private Long id;
private String name;
private UserEntity owner;
private Boolean isPublic;
private List<UserEntity> users;
}
6 changes: 4 additions & 2 deletions src/main/java/org/cryptodrink/domain/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ public String generateJWT(String username) throws IOException, NoSuchAlgorithmEx
}

private Boolean checkTokenOnCryptoHack(String username, String token) {
Optional<UserEntity> user = userService.find(username, false, true);
return user.map(userEntity -> userEntity.getWebsite().equals(token)).orElse(false);
UserEntity user = userService.find(username, false, true);
if (user == null)
return false;
return user.getWebsite().equals(token);
}

private String generateRandomValue() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;

@ApplicationScoped
public class ChallengeService {
Expand All @@ -24,12 +23,14 @@ public class ChallengeService {
@Inject
CategoryService categoryService;

public Optional<ChallengeEntity> find(String category, String name)
public ChallengeEntity find(String category, String name)
{
return challenges
ChallengeModel challenge = challenges
.find("category.name = ?1 AND name = ?2", category, name)
.firstResultOptional()
.map(challengeConverter::convert);
.firstResult();
if (challenge == null)
return null;
return challengeConverter.convert(challenge);
}

public List<SolvedChallengeEntity> getFlaggers(ChallengeEntity challenge)
Expand Down
33 changes: 18 additions & 15 deletions src/main/java/org/cryptodrink/domain/service/ScoreboardService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import javax.inject.Inject;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

@ApplicationScoped
public class ScoreboardService {
Expand Down Expand Up @@ -47,36 +46,40 @@ public List<ScoreboardEntity> getAll() {
return scoreboards.listAll().stream().map(scoreboardConverter::convert).toList();
}

public Optional<ScoreboardEntity> find(String name)
public ScoreboardEntity find(String name)
{
return scoreboards.find("LOWER(name)", name.toLowerCase())
.firstResultOptional()
.map(scoreboardConverter::convert);
ScoreboardModel scoreboard = scoreboards.find("LOWER(name)", name.toLowerCase())
.firstResult();
if (scoreboard == null)
return null;
return scoreboardConverter.convert(scoreboard);
}

@Transactional
public Optional<ScoreboardEntity> findAndDelete(String name) {
Optional<ScoreboardModel> model = scoreboards.find("LOWER(name)", name.toLowerCase())
.firstResultOptional();
if (model.isEmpty())
return Optional.empty();
scoreboards.deleteById(model.get().getId());
return Optional.of(scoreboardConverter.convert(model.get()));
public ScoreboardEntity findAndDelete(String name) {
ScoreboardModel model = scoreboards.find("LOWER(name)", name.toLowerCase())
.firstResult();
if (model == null)
return null;
scoreboards.deleteById(model.getId());
return scoreboardConverter.convert(model);
}

@Transactional
public Optional<ScoreboardEntity> create(String name)
public ScoreboardEntity create(String name, UserEntity owner)
{
ScoreboardModel model = new ScoreboardModel();
model.setName(name);
model.setOwner(users.findById(owner.getId()));
model.setIsPublic(false);
scoreboards.persist(model);
return Optional.of(scoreboardConverter.convert(model));
return scoreboardConverter.convert(model);
}

@Transactional
public ScoreboardEntity subscribeUser(ScoreboardEntity scoreboard, UserEntity user)
{
if (scoreboard.getUsers().stream().anyMatch(existingUser -> existingUser.getId() == user.getId()))
if (scoreboard.getUsers().stream().anyMatch(existingUser -> existingUser.getId().equals(user.getId())))
return scoreboard;
ScoreboardModel scoreboardModel = scoreboards.findById(scoreboard.getId());
UserModel userModel = users.findById(user.getId());
Expand Down
41 changes: 34 additions & 7 deletions src/main/java/org/cryptodrink/domain/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.cryptodrink.domain.service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.cryptodrink.converter.ScoreboardConverter;
import org.cryptodrink.converter.SolvedChallengeConverter;
import org.cryptodrink.converter.UserConverter;
Expand All @@ -11,11 +14,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@ApplicationScoped
public class UserService {
Expand All @@ -33,25 +39,27 @@ public class UserService {
ScoreboardConverter scoreboardConverter;
@Inject
CategoryService categoryService;
@Inject
AuthService authService;

public Optional<UserEntity> find(String username, Boolean databaseAllowed, Boolean apiAllowed)
public UserEntity find(String username, Boolean databaseAllowed, Boolean apiAllowed)
{
Optional<UserModel> user = Optional.empty();
UserModel user = null;
if (databaseAllowed) {
logger.debug("Looking for {} in database", username);
user = users
.find("LOWER(username)", username.toLowerCase())
.firstResultOptional();
.firstResult();
}
if (user.isEmpty()) {
if (user == null) {
logger.debug("User {} not found in database", username);
if (apiAllowed) {
cryptoHack.updateUserInfo(username);
return find(username, true, false);
}
return Optional.empty();
return null;
}
return Optional.of(userConverter.convert(user.get()));
return userConverter.convert(user);
}

public List<SolvedChallengeEntity> getSolvedChallenges(UserEntity user)
Expand Down Expand Up @@ -91,4 +99,23 @@ public List<CategoryCompletionResponse> getCompletion(UserEntity user) {

return completion;
}

public UserEntity getUserFromToken(String token) {
try {
String key = authService.getHMACKey();
SecretKey signingKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8));
Claims claims = Jwts.parser()
.verifyWith(signingKey)
.build()
.parseSignedClaims(token)
.getPayload();

String username = claims.getSubject();

return find(username, true, false);
} catch (Exception e) {
logger.error("Invalid JWT token");
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ public Response getChallenge(ChallengeRequest request)
String category = request.getCategory();
String name = request.getName();

Optional<ChallengeEntity> challenge = challengeService.find(category, name);
if (challenge.isEmpty())
ChallengeEntity challenge = challengeService.find(category, name);
if (challenge == null)
return Response.status(Response.Status.NOT_FOUND).entity("Challenge not found").build();
ChallengeResponse response = challengeConverter.convert(challenge.get());
ChallengeResponse response = challengeConverter.convert(challenge);
return Response.ok().entity(response).build();
}

Expand Down
Loading

0 comments on commit 026b7c2

Please sign in to comment.