1

Merge branch 'master' into change-to-polymorphie

This commit is contained in:
Lukas Ettel
2020-03-17 13:05:20 +01:00
11 changed files with 173 additions and 54 deletions

View File

@ -60,8 +60,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-oauth2-client', version: '2.2.5.RELEASE'
implementation 'org.keycloak:keycloak-spring-boot-starter:9.0.0' implementation 'org.keycloak:keycloak-spring-boot-starter:9.0.0'
implementation 'org.keycloak.bom:keycloak-adapter-bom:3.3.0.Final' implementation 'org.keycloak.bom:keycloak-adapter-bom:9.0.0'
implementation 'mops:styleguide:2.1.0' implementation 'mops:styleguide:2.1.0'
implementation 'io.springfox:springfox-swagger2:2.9.2' implementation 'io.springfox:springfox-swagger2:2.9.2'
implementation 'io.springfox:springfox-swagger-ui:2.9.2' implementation 'io.springfox:springfox-swagger-ui:2.9.2'
@ -73,6 +75,8 @@ dependencies {
runtimeOnly 'com.h2database:h2' runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java' runtimeOnly 'mysql:mysql-connector-java'
compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '2.4.0.RELEASE'
testImplementation 'org.assertj:assertj-core:3.15.0' testImplementation 'org.assertj:assertj-core:3.15.0'
testImplementation('org.springframework.boot:spring-boot-starter-test') { testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
@ -80,6 +84,7 @@ dependencies {
testImplementation 'org.springframework.security:spring-security-test' testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'com.tngtech.archunit:archunit-junit5:0.13.1' testImplementation 'com.tngtech.archunit:archunit-junit5:0.13.1'
implementation 'junit:junit:4.12' implementation 'junit:junit:4.12'
implementation 'junit:junit:4.12'
} }
test { test {

View File

@ -11,6 +11,7 @@ import mops.gruppen2.service.APIFormatterService;
import mops.gruppen2.service.EventService; import mops.gruppen2.service.EventService;
import mops.gruppen2.service.GroupService; import mops.gruppen2.service.GroupService;
import mops.gruppen2.service.SerializationService; import mops.gruppen2.service.SerializationService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@ -33,6 +34,7 @@ public class APIController {
} }
@GetMapping("/updateGroups/{status}") @GetMapping("/updateGroups/{status}")
@Secured("ROLE_api_user")
@ApiOperation(value = "Gibt alle Gruppen zurück in denen sich etwas geändert hat") @ApiOperation(value = "Gibt alle Gruppen zurück in denen sich etwas geändert hat")
public UpdatedGroupRequestMapper updateGroup(@ApiParam("Letzter Status des Anfragestellers") @PathVariable Long status) throws EventException { public UpdatedGroupRequestMapper updateGroup(@ApiParam("Letzter Status des Anfragestellers") @PathVariable Long status) throws EventException {
List<Event> events = eventService.getNewEvents(status); List<Event> events = eventService.getNewEvents(status);
@ -42,12 +44,14 @@ public class APIController {
} }
@GetMapping("/getGroupIdsOfUser/{teilnehmer}") @GetMapping("/getGroupIdsOfUser/{teilnehmer}")
@Secured("ROLE_api_user")
@ApiOperation(value = "Gibt alle Gruppen zurück in denen sich ein Teilnehmer befindet") @ApiOperation(value = "Gibt alle Gruppen zurück in denen sich ein Teilnehmer befindet")
public List<Long> getGroupsOfUser(@ApiParam("Teilnehmer dessen groupIds zurückgegeben werden sollen") @PathVariable String teilnehmer) throws EventException { public List<Long> getGroupsOfUser(@ApiParam("Teilnehmer dessen groupIds zurückgegeben werden sollen") @PathVariable String teilnehmer) throws EventException {
return eventService.getGroupsOfUser(teilnehmer); return eventService.getGroupsOfUser(teilnehmer);
} }
@GetMapping("/getGroup/{groupId}") @GetMapping("/getGroup/{groupId}")
@Secured("ROLE_api_user")
@ApiOperation(value = "Gibt die Gruppe mit der als Parameter mitgegebenden groupId zurück") @ApiOperation(value = "Gibt die Gruppe mit der als Parameter mitgegebenden groupId zurück")
public Group getGroupFromId(@ApiParam("GruppenId der gefordeten Gruppe") @PathVariable Long groupId) throws EventException{ public Group getGroupFromId(@ApiParam("GruppenId der gefordeten Gruppe") @PathVariable Long groupId) throws EventException{
List<Event> eventList = eventService.getEventsOfGroup(groupId); List<Event> eventList = eventService.getEventsOfGroup(groupId);

View File

@ -3,8 +3,7 @@ package mops.gruppen2.controller;
import mops.gruppen2.config.Gruppen2Config; import mops.gruppen2.config.Gruppen2Config;
import mops.gruppen2.domain.Exceptions.EventException; import mops.gruppen2.domain.Exceptions.EventException;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.security.Account; import mops.gruppen2.security.Account;
@ -15,12 +14,12 @@ import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ResponseStatusException;
import javax.annotation.security.RolesAllowed; import javax.annotation.security.RolesAllowed;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
@Controller @Controller
@RequestMapping("/gruppen2") @RequestMapping("/gruppen2")
@ -114,4 +113,16 @@ public class Gruppen2Controller {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group not found"); throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group not found");
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator)"})
@GetMapping("/detailsSearch")
public String showGroupDetailsNoMember (KeycloakAuthenticationToken token, Model model, @RequestParam (value="id") Long id) throws EventException {
model.addAttribute("account", keyCloakService.createAccountFromPrincipal(token));
Group group = userService.getGroupById(id);
if (group!=null) {
model.addAttribute("group", group);
return "detailsNoMember";
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group not found");
}
} }

View File

@ -1,8 +1,13 @@
package mops.gruppen2.security; package mops.gruppen2.security;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.web.client.RestTemplate;
/** /**
* WORKAROUND for https://issues.redhat.com/browse/KEYCLOAK-11282 * WORKAROUND for https://issues.redhat.com/browse/KEYCLOAK-11282
@ -15,4 +20,35 @@ public class KeycloakConfig {
public KeycloakSpringBootConfigResolver keycloakConfigResolver() { public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver(); return new KeycloakSpringBootConfigResolver();
} }
@Value("${keycloak.resource}")
private String clientId;
@Value("${keycloak.credentials.secret}")
private String clientSecret;
@Value("${hhu_keycloak.token-uri}")
private String tokenUri;
@Bean
public RestTemplate serviceAccountRestTemplate() {
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setGrantType(OAuth2Constants.CLIENT_CREDENTIALS);
resourceDetails.setAccessTokenUri(tokenUri);
resourceDetails.setClientId(clientId);
resourceDetails.setClientSecret(clientSecret);
return new OAuth2RestTemplate(resourceDetails);
}
} }

View File

@ -2,8 +2,6 @@ package mops.gruppen2.service;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import mops.gruppen2.domain.EventDTO; import mops.gruppen2.domain.EventDTO;
import mops.gruppen2.domain.Exceptions.EventException;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.Visibility; import mops.gruppen2.domain.Visibility;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
@ -23,17 +21,19 @@ public class EventService {
this.eventStore = eventStore; this.eventStore = eventStore;
} }
/** sichert ein Event Objekt indem es ein EventDTO Objekt erzeugt /**
* sichert ein Event Objekt indem es ein EventDTO Objekt erzeugt
* *
* @param event * @param event
*/ */
public void saveEvent(Event event){ public void saveEvent(Event event) {
EventDTO eventDTO = getDTO(event); EventDTO eventDTO = getDTO(event);
eventStore.save(eventDTO); eventStore.save(eventDTO);
} }
/** Erzeugt aus einem Event Objekt ein EventDTO Objekt. /**
* Ist die Gruppe öffentlich, dann wird die visibility auf true gesetzt. * Erzeugt aus einem Event Objekt ein EventDTO Objekt.
* Ist die Gruppe öffentlich, dann wird die visibility auf true gesetzt.
* *
* @param event * @param event
* @return EventDTO * @return EventDTO
@ -42,24 +42,24 @@ public class EventService {
EventDTO eventDTO = new EventDTO(); EventDTO eventDTO = new EventDTO();
eventDTO.setGroup_id(event.getGroup_id()); eventDTO.setGroup_id(event.getGroup_id());
eventDTO.setUser_id(event.getUser_id()); eventDTO.setUser_id(event.getUser_id());
if(event instanceof CreateGroupEvent) { if (event instanceof CreateGroupEvent) {
if(((CreateGroupEvent) event).getGroupVisibility() == Visibility.PRIVATE) { if (((CreateGroupEvent) event).getGroupVisibility() == Visibility.PRIVATE) {
eventDTO.setVisibility(false); eventDTO.setVisibility(false);
}else { } else {
eventDTO.setVisibility(true); eventDTO.setVisibility(true);
} }
} }
try { try {
eventDTO.setEvent_payload(serializationService.serializeEvent(event)); eventDTO.setEvent_payload(serializationService.serializeEvent(event));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
e.printStackTrace(); e.printStackTrace();
} }
return eventDTO; return eventDTO;
} }
/** Sorgt dafür die Group_id immer um 1 zu erhöhen /**
* Sorgt dafür die Group_id immer um 1 zu erhöhen
* *
* @return Gibt Long zurück * @return Gibt Long zurück
*/ */
@ -77,30 +77,32 @@ public class EventService {
return tmpId; return tmpId;
} }
/** Findet alle Events welche ab dem neuen Status hinzugekommen sind /**
* Findet alle Events welche ab dem neuen Status hinzugekommen sind
* *
* @param status * @param status
* @return Liste von Events * @return Liste von Events
*/ */
public List<Event> getNewEvents(Long status){ public List<Event> getNewEvents(Long status) {
List<Long> groupIdsThatChanged = eventStore.findNewEventSinceStatus(status); List<Long> groupIdsThatChanged = eventStore.findNewEventSinceStatus(status);
List<EventDTO> groupEventDTOS = eventStore.findAllEventsOfGroups(groupIdsThatChanged); List<EventDTO> groupEventDTOS = eventStore.findAllEventsOfGroups(groupIdsThatChanged);
return translateEventDTOs(groupEventDTOS); return translateEventDTOs(groupEventDTOS);
} }
/** Erzeugt aus einer Liste von eventDTOs eine Liste von Events /**
* Erzeugt aus einer Liste von eventDTOs eine Liste von Events
* *
* @param eventDTOS * @param eventDTOS
* @return Liste von Events * @return Liste von Events
*/ */
public List<Event> translateEventDTOs(Iterable<EventDTO> eventDTOS){ public List<Event> translateEventDTOs(Iterable<EventDTO> eventDTOS) {
List<Event> events = new ArrayList<>(); List<Event> events = new ArrayList<>();
for (EventDTO eventDTO : eventDTOS) { for (EventDTO eventDTO : eventDTOS) {
try { try {
events.add(serializationService.deserializeEvent(eventDTO.getEvent_payload())); events.add(serializationService.deserializeEvent(eventDTO.getEvent_payload()));
}catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -113,12 +115,12 @@ public class EventService {
* @param createGroupEvents Liste von Event Objekten * @param createGroupEvents Liste von Event Objekten
*/ */
public void saveEventList(List<Event> createGroupEvents) { public void saveEventList(List<Event> createGroupEvents) {
for(Event event : createGroupEvents) { for (Event event : createGroupEvents) {
saveEvent(event); saveEvent(event);
} }
} }
public Long getMaxEvent_id(){ public Long getMaxEvent_id() {
return eventStore.getHighesEvent_ID(); return eventStore.getHighesEvent_ID();
} }

View File

@ -12,5 +12,12 @@ spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
keycloak.principal-attribute=preferred_username keycloak.principal-attribute=preferred_username
keycloak.auth-server-url=https://keycloak.cs.hhu.de/auth keycloak.auth-server-url=https://keycloak.cs.hhu.de/auth
keycloak.realm=MOPS keycloak.realm=MOPS
keycloak.resource=demo
keycloak.public-client=true hhu_keycloak.token-uri=https://keycloak.cs.hhu.de/auth/realms/MOPS/protocol/openid-connect/token
keycloak.resource=gruppenfindung
keycloak.credentials.secret= fc6ebf10-8c63-4e71-a667-4eae4e8209a1
keycloak.verify-token-audience=true
keycloak.use-resource-role-mappings=true
keycloak.autodetect-bearer-only=true
keycloak.confidential-port= 443

View File

@ -10,5 +10,12 @@ spring.datasource.password=geheim
keycloak.principal-attribute=preferred_username keycloak.principal-attribute=preferred_username
keycloak.auth-server-url=https://keycloak.cs.hhu.de/auth keycloak.auth-server-url=https://keycloak.cs.hhu.de/auth
keycloak.realm=MOPS keycloak.realm=MOPS
keycloak.resource=demo
keycloak.public-client=true hhu_keycloak.token-uri=https://keycloak.cs.hhu.de/auth/realms/MOPS/protocol/openid-connect/token
keycloak.resource=gruppenfindung
keycloak.credentials.secret= fc6ebf10-8c63-4e71-a667-4eae4e8209a1
keycloak.verify-token-audience=true
keycloak.use-resource-role-mappings=true
keycloak.autodetect-bearer-only=true
keycloak.confidential-port= 443

View File

@ -1 +1,2 @@
spring.profiles.active=dev spring.profiles.active=dev

View File

@ -25,7 +25,26 @@
</nav> </nav>
</header> </header>
<main th:fragment="bodycontent"> <main th:fragment="bodycontent">
<div class="container-fluid">
<div class="row">
<div class="col-9" style="border: 10px solid aliceblue; background: aliceblue">
<form action="/" method="get">
<h1 style="color: dodgerblue; font-weight: bold" th:text="${group.getTitle()}"></h1>
<p style="font-weight: bold">
<span class="badge badge-pill badge-dark" style="background: darkslategray" th:if="${group.getVisibility() == group.getVisibility().PRIVATE }">Private Gruppe</span>
<span class="badge badge-pill badge-primary" th:if="${group.getVisibility() == group.getVisibility().PUBLIC}">Öffentliche Gruppe</span>
<span class="badge badge-pill badge-success" style="background: lightseagreen" th:if="${group.getType() == group.getType().LECTURE}"> Veranstaltung</span>
</p>
<p th:text="${group.getDescription()}"></p>
<div class="form-group">
<div class="text-right">
<button class="btn btn-primary" style="border-style: none;">Gruppe beitreten</button>
</div>
</div>
</form>
</div>
</div>
</div>
</main> </main>
</body> </body>
</html> </html>

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
<div class="mx-auto" style="vertical-align: center; horiz-align: center; top: 50%; left: 50%; margin-top: 200px">
<div class="text-center" style="background: aliceblue; align-items: center; margin: auto; width: 1000px; vertical-align: center; padding: 50px; display: block">
<h1 style="text-align: center">Da ist etwas schiefgelaufen!</h1>
<h2 style="text-align: center">Die Seite, nach der du suchst, scheint nicht zu existieren.</h2>
<br>
<div>
<button type="button" class="btn btn-primary" style="margin: auto">
<a style="color: white" href="#" onclick="javascript:window.history.back(-1);return false;">Zurück zur letzten Seite</a>
</button>
</div>
<br>
</div>
</div>
</body>
</html>

View File

@ -35,30 +35,31 @@
<label for="suchleiste">Suchbegriff:</label> <label for="suchleiste">Suchbegriff:</label>
<input id="suchleiste" class="form-control" placeholder="z.B. Programmieren, Lerngruppe, ..." th:name="suchbegriff" type="text"> <input id="suchleiste" class="form-control" placeholder="z.B. Programmieren, Lerngruppe, ..." th:name="suchbegriff" type="text">
</div> </div>
<button type="submit" class="btn btn-primary" style="background: #52a1eb; border-style: none">Suchen</button> </form>
</div> <br>
</form> <table class="table">
<br> <!-- Erscheint dann, wenn man "Suchen" Button klickt und Ergebnisse angezeigt werden, aber so solls aussehen -->
<table class="table"> <thead th:if="${!gruppen.isEmpty()}">
<!-- Erscheint dann, wenn man "Suchen" Button klickt und Ergebnisse angezeigt werden, aber so solls aussehen --> <tr>
<thead th:if="${!gruppen.isEmpty()}"> <th scope="col">Gruppenname</th>
<tr> <th scope="col">Beschreibung</th>
<th scope="col">Gruppenname</th> <th scope="col">Öffentlich/Privat</th>
<th scope="col">Beschreibung</th> <th scope="col">Mitgliederanzahl</th>
<th scope="col">Öffentlich/Privat</th> </tr>
<th scope="col">Mitgliederanzahl</th> </thead>
</tr> <tbody th:each="gruppe : ${gruppen}">
</thead> <tr>
<tbody th:each="gruppe : ${gruppen}"> <th scope="row">
<tr> <a th:href="@{/gruppen2/detailsSearch(id=${gruppe.getId()})}" th:text="${gruppe.title}">Gruppenname</a>
<th scope="row" th:text="${gruppe.title}">Gruppenname</th> </th>
<td th:text="${gruppe.getDescription()}">Beschreibung</td> <td th:text="${gruppe.getDescription()}">Beschreibung</td>
<td th:text="${gruppe.getVisibility()}">Öffentlich</td> <td th:text="${gruppe.getVisibility()}">Öffentlich</td>
<td th:text="${gruppe.getMembers().size()}">Mitgliederanzahl</td> <td th:text="${gruppe.getMembers().size()}">Mitgliederanzahl</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</main> </main>