Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Admin view #211

Merged
merged 14 commits into from
Apr 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class QuestionGeneratorV2 implements QuestionGenerator{

Expand All @@ -27,6 +29,8 @@ public class QuestionGeneratorV2 implements QuestionGenerator{
private String answer_placeholder;
private String language;

private Random random = new SecureRandom();

public QuestionGeneratorV2(JsonNode jsonNode) {
this.jsonNode = jsonNode;
this.language_placeholder = jsonNode.get("language_placeholder").textValue();
Expand Down Expand Up @@ -103,8 +107,8 @@ private List<Answer> generateOptions(JsonNode results, String correctAnswer, Str
int size = results.size();
int tries = 0;

while (options.size() < 3 && tries < 10){
int random = (int) (Math.random() * size);
while (options.size() < 3 && tries < 10) {
int random = (int) (this.random.nextFloat() * size);
String option = results.get(random).path(answerLabel).path("value").asText();
if (!option.equals(correctAnswer) && !usedOptions.contains(option) ) {
usedOptions.add(option);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/uniovi/configuration/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers("/api/**").permitAll()
.requestMatchers("/game/**").authenticated()
.requestMatchers("/ranking/playerRanking").authenticated()
.requestMatchers("/player/admin/**").hasAuthority("ROLE_ADMIN")
.requestMatchers("/**").permitAll()
).formLogin(
form -> form
Expand Down
11 changes: 1 addition & 10 deletions src/main/java/com/uniovi/controllers/GameController.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,23 @@ public String getCheckResult(@PathVariable Long idQuestion, @PathVariable Long i

if(idAnswer == -1
|| getRemainingTime(gameSession) <= 0) {
//model.addAttribute("correctAnswer", gameSession.getCurrentQuestion().getCorrectAnswer());
//model.addAttribute("messageKey", "timeRunOut.result");
//model.addAttribute("logoImage", "/images/logo_incorrect.svg");
gameSession.addAnsweredQuestion(gameSession.getCurrentQuestion());
gameSession.addQuestion(false, 0);
}
else if(questionService.checkAnswer(idQuestion, idAnswer)) {
//model.addAttribute("messageKey", "correctAnswer.result");
//model.addAttribute("logoImage", "/images/logo_correct.svg");

if (!gameSession.isAnswered(gameSession.getCurrentQuestion())) {
gameSession.addQuestion(true, getRemainingTime(gameSession));
gameSession.addAnsweredQuestion(gameSession.getCurrentQuestion());
}

} else {
//model.addAttribute("correctAnswer", gameSession.getCurrentQuestion().getCorrectAnswer());
//model.addAttribute("messageKey", "failedAnswer.result");
//model.addAttribute("logoImage", "/images/logo_incorrect.svg");
gameSession.addAnsweredQuestion(gameSession.getCurrentQuestion());
gameSession.addQuestion(false, 0);
}

session.setAttribute("hasJustAnswered", true);
gameSession.getNextQuestion();
//return "game/fragments/questionResult";

return updateGame(model, session);
}

Expand Down
178 changes: 171 additions & 7 deletions src/main/java/com/uniovi/controllers/PlayersController.java
Original file line number Diff line number Diff line change
@@ -1,42 +1,56 @@
package com.uniovi.controllers;

import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.uniovi.configuration.SecurityConfig;
import com.uniovi.dto.RoleDto;
import com.uniovi.entities.Associations;
import com.uniovi.entities.GameSession;
import com.uniovi.entities.Player;
import com.uniovi.services.GameSessionService;
import com.uniovi.services.PlayerService;
import com.uniovi.entities.Role;
import com.uniovi.services.*;
import com.uniovi.validators.SignUpValidator;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.*;
import com.uniovi.dto.PlayerDto;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Principal;
import java.util.Optional;
import java.util.List;

@Controller
public class PlayersController {
private final PlayerService playerService;
private final RoleService roleService;
private QuestionService questionService;
private final SignUpValidator signUpValidator;

private final GameSessionService gameSessionService;

@Autowired
public PlayersController(PlayerService playerService, SignUpValidator signUpValidator, GameSessionService gameSessionService) {
public PlayersController(PlayerService playerService, SignUpValidator signUpValidator, GameSessionService gameSessionService,
RoleService roleService, QuestionService questionService) {
this.playerService = playerService;
this.signUpValidator = signUpValidator;
this.gameSessionService = gameSessionService;
this.roleService = roleService;
}

@GetMapping("/signup")
Expand Down Expand Up @@ -122,4 +136,154 @@ public String showPlayerRanking(Pageable pageable, Model model, Principal princi

return "ranking/playerRanking";
}

// ----- Admin endpoints -----

@GetMapping("/player/admin")
public String showAdminPanel(Model model) {
return "player/admin/admin";
}

@GetMapping("/player/admin/userManagement")
public String showUserManagementFragment(Model model, Pageable pageable) {
model.addAttribute("endpoint", "/player/admin/userManagement");
Page<Player> users = playerService.getPlayersPage(pageable);
model.addAttribute("page", users);
model.addAttribute("users", users.getContent());

return "player/admin/userManagement";
}

@GetMapping("/player/admin/deleteUser")
@ResponseBody
public String deleteUser(HttpServletResponse response, @RequestParam String username, Principal principal) {
Player player = playerService.getUserByUsername(username).orElse(null);
if (player == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "User not found";
}

if (principal.getName().equals(player.getUsername())) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return "You can't delete yourself";
}

playerService.deletePlayer(player.getId());
return "User deleted";
}

@GetMapping("/player/admin/changePassword")
@ResponseBody
public String changePassword(HttpServletResponse response, @RequestParam String username, @RequestParam String password) {
Player player = playerService.getUserByUsername(username).orElse(null);
if (player == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "User not found";
}

playerService.updatePassword(player, password);
return "User password changed";
}

@GetMapping("/player/admin/getRoles")
@ResponseBody
public String getRoles(@RequestParam String username) {
List<Role> roles = roleService.getAllRoles();
Player player = playerService.getUserByUsername(username).orElse(null);

roles.remove(roleService.getRole("ROLE_USER"));

if (player == null) {
return "{}";
}

ObjectMapper mapper = new ObjectMapper();
ObjectNode rolesJson = mapper.createObjectNode();
for (Role role : roles) {
boolean hasRole = player.getRoles().contains(role);
rolesJson.put(role.getName(), hasRole);
}

return rolesJson.toString();
}

@GetMapping("/player/admin/changeRoles")
@ResponseBody
public String changeRoles(HttpServletResponse response, @RequestParam String username, @RequestParam String roles) {
Player player = playerService.getUserByUsername(username).orElse(null);
if (player == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "User not found";
}

JsonNode rolesJson;
try {
rolesJson = new ObjectMapper().readTree(roles);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "Invalid roles";
}

rolesJson.fieldNames().forEachRemaining(roleName -> {
boolean hasRole = rolesJson.get(roleName).asBoolean();

Role role = roleService.getRole(roleName);
if (role == null && !hasRole) {
return;
} else if (role == null) {
role = roleService.addRole(new RoleDto(roleName));
}

if (hasRole) {
Associations.PlayerRole.addRole(player, role);
} else {
Associations.PlayerRole.removeRole(player, role);
}
});

playerService.savePlayer(player);
return "User roles changed";
}

@GetMapping("/player/admin/questionManagement")
public String showQuestionManagementFragment(Model model) throws IOException {
File jsonFile = new File(QuestionGeneratorService.jsonFilePath);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode json = objectMapper.readTree(jsonFile);
model.addAttribute("jsonContent", json.toString());

return "player/admin/questionManagement";
}

@GetMapping("/player/admin/deleteAllQuestions")
@ResponseBody
public String deleteAllQuestions() {
questionService.deleteAllQuestions();
return "Questions deleted";
}

@GetMapping("/player/admin/saveQuestions")
@ResponseBody
public String saveQuestions(HttpServletResponse response, @RequestParam String json) throws IOException {
try {
JsonNode node = new ObjectMapper().readTree(json);
DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter();
printer.indentObjectsWith(indenter); // Indent JSON objects
printer.indentArraysWith(indenter); // Indent JSON arrays

ObjectMapper mapper = new ObjectMapper();
mapper.writer(printer).writeValue(new FileOutputStream(QuestionGeneratorService.jsonFilePath), node);
return "Questions saved";
}
catch (Exception e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "Invalid JSON";
}
}

@GetMapping("/player/admin/monitoring")
public String showMonitoring(Model model) {
return "player/admin/monitoring";
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/uniovi/entities/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ public class Role {
public Role(String name) {
this.name = name;
}

@Override
public String toString() {
return name;
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/uniovi/repositories/PlayerRepository.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.uniovi.repositories;

import com.uniovi.entities.Player;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;

public interface PlayerRepository extends CrudRepository<Player, Long> {
Player findByEmail(String email);
Player findByUsername(String nickname);

Page<Player> findAll(Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ public InsertSampleDataService(PlayerService playerService, QuestionService ques
@Transactional
@EventListener(ApplicationReadyEvent.class) // Uncomment this line to insert sample data on startup
public void insertSampleQuestions() throws InterruptedException, IOException {
if (!playerService.getUserByEmail("[email protected]").isPresent()) {
if (playerService.getUserByEmail("[email protected]").isEmpty()) {
PlayerDto player = new PlayerDto();
player.setEmail("[email protected]");
player.setUsername("test");
player.setPassword("test");
player.setRoles(new String[]{"ROLE_USER"});
if (Arrays.asList(environment.getActiveProfiles()).contains("test"))
player.setRoles(new String[]{"ROLE_USER", "ROLE_ADMIN"});
else
player.setRoles(new String[]{"ROLE_USER"});
playerService.generateApiKey(playerService.addNewPlayer(player));
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/uniovi/services/PlayerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.uniovi.entities.Player;
import com.uniovi.repositories.PlayerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;
Expand Down Expand Up @@ -72,4 +74,24 @@ public interface PlayerService {
* @param id The id of the player to delete
*/
void deletePlayer(Long id);

/**
* Get a page with all the players in the database
* @param pageable The page information
* @return A page with all the players
*/
Page<Player> getPlayersPage(Pageable pageable);

/**
* Update the password of a player
* @param player The player to update the password
* @param password The new password
*/
void updatePassword(Player player, String password);

/**
* Save a player in the database
* @param player The player to save
*/
void savePlayer(Player player);
}
Loading
Loading