draft for borrow, update partey tags, added jwt

This commit is contained in:
_Bastler 2021-10-22 10:56:20 +02:00
parent 442bdb4996
commit a24f0650d1
41 changed files with 3415 additions and 162 deletions

View File

@ -12,6 +12,11 @@
<artifactId>application</artifactId>
<dependencies>
<dependency>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-borrow</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-core</artifactId>

26
borrow/pom.xml Normal file
View File

@ -0,0 +1,26 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-main</artifactId>
<version>${revision}</version>
</parent>
<name>borrow</name>
<artifactId>webstly-borrow</artifactId>
<dependencies>
<dependency>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-email</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-jwt</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,245 @@
/**
*
*/
package de.bstly.we.borrow.businesslogic;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.beust.jcommander.internal.Lists;
import com.querydsl.core.BooleanBuilder;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowItemManualSlot;
import de.bstly.we.borrow.model.BorrowItemPeriodSlot;
import de.bstly.we.borrow.model.BorrowItemSlot;
import de.bstly.we.borrow.model.QBorrowItem;
import de.bstly.we.borrow.model.QBorrowItemManualSlot;
import de.bstly.we.borrow.model.QBorrowItemPeriodSlot;
import de.bstly.we.borrow.repository.BorrowItemManualSlotRepository;
import de.bstly.we.borrow.repository.BorrowItemPeriodSlotRepository;
import de.bstly.we.borrow.repository.BorrowItemRepository;
import de.bstly.we.businesslogic.UserManager;
import de.bstly.we.email.businesslogic.EmailManager;
import de.bstly.we.model.User;
/**
* The Class BorrowItemManager.
*/
@Component
public class BorrowItemManager {
@Autowired
private BorrowItemRepository borrowItemRepository;
@Autowired
private BorrowItemManualSlotRepository borrowItemManualSlotRepository;
@Autowired
private BorrowItemPeriodSlotRepository borrowItemPeriodSlotRepository;
@Autowired
private UserManager userManager;
@Autowired
private EmailManager emailManager;
private QBorrowItem qBorrowItem = QBorrowItem.borrowItem;
private QBorrowItemManualSlot qBorrowItemManualSlot = QBorrowItemManualSlot.borrowItemManualSlot;
private QBorrowItemPeriodSlot qBorrowItemPeriodSlot = QBorrowItemPeriodSlot.borrowItemPeriodSlot;
/**
* Exists.
*
* @param id the id
* @return true, if successful
*/
public boolean exists(Long id) {
return borrowItemRepository.existsById(id);
}
/**
* Gets the.
*
* @param id the id
* @return the borrow item
*/
public BorrowItem get(Long id) {
return borrowItemRepository.findById(id).orElse(null);
}
public void applySlots(BorrowItem borrowItem) {
if (borrowItem != null) {
switch (borrowItem.getAvailability()) {
case MANUAL:
borrowItem.setSlots((List<? extends BorrowItemSlot>) borrowItemManualSlotRepository
.findAll(qBorrowItemManualSlot.item.eq(borrowItem.getId())));
break;
case PERIOD:
borrowItem.setSlots((List<? extends BorrowItemSlot>) borrowItemPeriodSlotRepository
.findAll(qBorrowItemPeriodSlot.item.eq(borrowItem.getId())));
break;
default:
break;
}
if (borrowItem.getSlots() == null) {
borrowItem.setSlots(Lists.newArrayList());
}
}
}
/**
* Gets the.
*
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @param search the search
* @return the page
*/
public Page<BorrowItem> get(int page, int size, String sortBy, boolean descending,
String search) {
if (StringUtils.hasText(search)) {
return borrowItemRepository.findAll(
qBorrowItem.name.contains(search).or(qBorrowItem.description.contains(search)),
PageRequest.of(page, size, descending ? Sort.by(sortBy).descending()
: Sort.by(sortBy).ascending()));
}
return borrowItemRepository.findAll(PageRequest.of(page, size,
descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending()));
}
/**
* Gets the for user.
*
* @param userId the user id
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @param search the search
* @return the for user
*/
public Page<BorrowItem> getForUser(Long userId, int page, int size, String sortBy,
boolean descending, String search) {
BooleanBuilder query = new BooleanBuilder();
query.and(qBorrowItem.owner.eq(userId));
if (StringUtils.hasText(search)) {
query.and(
qBorrowItem.name.contains(search).or(qBorrowItem.description.contains(search)));
}
return borrowItemRepository.findAll(query.getValue(), PageRequest.of(page, size,
descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending()));
}
/**
* Save.
*
* @param borrowItem the borrow item
* @return the borrow item
*/
public BorrowItem save(BorrowItem borrowItem) {
if (borrowItem.getId() != null && !borrowItem.getId().equals(0L)) {
borrowItemManualSlotRepository.deleteAll(borrowItemManualSlotRepository
.findAll(qBorrowItemManualSlot.item.eq(borrowItem.getId())));
borrowItemPeriodSlotRepository.deleteAll(borrowItemPeriodSlotRepository
.findAll(qBorrowItemPeriodSlot.item.eq(borrowItem.getId())));
}
List<? extends BorrowItemSlot> slots = borrowItem.getSlots();
borrowItem = borrowItemRepository.save(borrowItem);
if (borrowItem != null) {
borrowItem.setSlots(slots);
if (borrowItem.getSlots() == null) {
borrowItem.setSlots(Lists.newArrayList());
}
List<BorrowItemSlot> invalidBorrowItemSlots = Lists.newArrayList();
for (BorrowItemSlot borrowItemSlot : borrowItem.getSlots()) {
borrowItemSlot.setItem(borrowItem.getId());
switch (borrowItem.getAvailability()) {
case MANUAL:
if (borrowItemSlot instanceof BorrowItemManualSlot) {
borrowItemManualSlotRepository.save((BorrowItemManualSlot) borrowItemSlot);
} else {
invalidBorrowItemSlots.add(borrowItemSlot);
}
break;
case PERIOD:
if (borrowItemSlot instanceof BorrowItemPeriodSlot) {
borrowItemPeriodSlotRepository.save((BorrowItemPeriodSlot) borrowItemSlot);
} else {
invalidBorrowItemSlots.add(borrowItemSlot);
}
break;
default:
break;
}
}
borrowItem.getSlots().removeAll(invalidBorrowItemSlots);
}
return borrowItem;
}
/**
* Delete.
*
* @param borrowItem the borrow item
*/
public void delete(BorrowItem borrowItem) {
borrowItemManualSlotRepository.deleteAll(borrowItemManualSlotRepository
.findAll(qBorrowItemManualSlot.item.eq(borrowItem.getId())));
borrowItemPeriodSlotRepository.deleteAll(borrowItemPeriodSlotRepository
.findAll(qBorrowItemPeriodSlot.item.eq(borrowItem.getId())));
borrowItemRepository.delete(borrowItem);
}
/**
* Delete.
*
* @param id the id
*/
public void delete(Long id) {
borrowItemRepository.deleteById(id);
}
/**
* Notify owner.
*
* @param borrowItem the borrow item
*/
public void notifyOwner(BorrowItem borrowItem) {
Assert.isTrue(
borrowItem.getEmailNotification() != null
&& borrowItem.getEmailNotification().booleanValue(),
"Email notification not enabled for '"
+ borrowItem.getId()
+ "'!");
String email = borrowItem.getEmail();
if (!StringUtils.hasText(email)) {
User user = userManager.get(borrowItem.getOwner());
Assert.notNull(user, "invalid owner");
email = emailManager.getUserEmail(user);
}
// TODO: send email
}
}

View File

@ -0,0 +1,13 @@
/**
*
*/
package de.bstly.we.borrow.businesslogic;
/**
* The Interface BorrowPermissions.
*/
public interface BorrowPermissions {
public static final String BORROW_ITEMS = "borrow_items";
public static final String BORROW_REQUESTS = "borrow_requests";
}

View File

@ -0,0 +1,242 @@
/**
*
*/
package de.bstly.we.borrow.businesslogic;
import java.text.ParseException;
import java.time.Instant;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.KeySourceException;
import com.nimbusds.jwt.JWTClaimsSet.Builder;
import com.nimbusds.jwt.SignedJWT;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowRequest;
import de.bstly.we.borrow.model.BorrowRequestStatus;
import de.bstly.we.borrow.model.QBorrowRequest;
import de.bstly.we.borrow.repository.BorrowRequestRepository;
import de.bstly.we.businesslogic.UserManager;
import de.bstly.we.jwt.businesslogic.JwtKeyManager;
import de.bstly.we.jwt.model.JwtKey;
import de.bstly.we.model.User;
/**
* The Class BorrowRequestManager.
*/
@Component
public class BorrowRequestManager {
@Autowired
private BorrowRequestRepository borrowRequestRepository;
@Autowired
private BorrowItemManager borrowItemManager;
@Autowired
private JwtKeyManager jwtKeyManager;
@Autowired
private UserManager userManager;
private QBorrowRequest qBorrowRequest = QBorrowRequest.borrowRequest;
public static final String JWT_BORROW_KEY_NAME = "borrow";
/**
* Gets the.
*
* @param id the id
* @return the borrow request
*/
public BorrowRequest get(Long id) {
return borrowRequestRepository.findById(id).orElse(null);
}
/**
* Gets the for user.
*
* @param userId the user id
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @return the for user
*/
public Page<BorrowRequest> getForUser(Long userId, int page, int size, String sortBy,
boolean descending) {
return borrowRequestRepository.findAll(qBorrowRequest.user.eq(userId), PageRequest.of(page,
size, descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending()));
}
/**
* Gets the for user and stauts.
*
* @param userId the user id
* @param status the status
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @return the for user and stauts
*/
public Page<BorrowRequest> getForUserAndStauts(Long userId, BorrowRequestStatus status,
int page, int size, String sortBy, boolean descending) {
return borrowRequestRepository.findAll(
qBorrowRequest.user.eq(userId).and(qBorrowRequest.status.eq(status)),
PageRequest.of(page, size,
descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending()));
}
/**
* Gets the for owner.
*
* @param userId the user id
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @return the for owner
*/
public Page<BorrowRequest> getForOwner(Long userId, int page, int size, String sortBy,
boolean descending) {
return borrowRequestRepository.findAllByOwner(userId, PageRequest.of(page, size,
descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending()));
}
/**
* Gets the for owner and status.
*
* @param userId the user id
* @param status the status
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @return the for owner and status
*/
public Page<BorrowRequest> getForOwnerAndStatus(Long userId, BorrowRequestStatus status,
int page, int size, String sortBy, boolean descending) {
return borrowRequestRepository.findAllByOwnerAndStatus(userId, status, PageRequest.of(page,
size, descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending()));
}
/**
* Save.
*
* @param borrowRequest the borrow request
* @return the borrow request
*/
public BorrowRequest save(BorrowRequest borrowRequest) {
return borrowRequestRepository.save(borrowRequest);
}
/**
* Delete.
*
* @param borrowRequest the borrow request
*/
public void delete(BorrowRequest borrowRequest) {
borrowRequestRepository.delete(borrowRequest);
}
/**
* Delete.
*
* @param id the id
*/
public void delete(Long id) {
borrowRequestRepository.deleteById(id);
}
/**
* Creates the code.
*
* @param borrowRequest the borrow request
* @param issuer the issuer
* @return the signed JWT
* @throws JOSEException the JOSE exception
*/
public SignedJWT createCode(BorrowRequest borrowRequest, String issuer) throws JOSEException {
BorrowItem borrowItem = borrowItemManager.get(borrowRequest.getItem());
User owner = userManager.get(borrowItem.getOwner());
User user = userManager.get(borrowRequest.getUser());
borrowRequest.setBorrowItem(borrowItem);
Builder claimsSetBuilder = new Builder();
claimsSetBuilder.audience(JWT_BORROW_KEY_NAME);
// issuer
claimsSetBuilder.issuer(issuer);
claimsSetBuilder.issueTime(Date.from(Instant.now()));
// item
claimsSetBuilder.subject(String.valueOf(borrowItem.getId()));
claimsSetBuilder.claim("name", borrowItem.getName());
// dates
claimsSetBuilder.expirationTime(Date.from(borrowRequest.getEnds()));
claimsSetBuilder.notBeforeTime(Date.from(borrowRequest.getStarts()));
// owner
claimsSetBuilder.claim("owner", owner.getUsername());
// user
claimsSetBuilder.claim("user", user.getUsername());
// status
claimsSetBuilder.claim("status", borrowRequest.getStatus());
if (StringUtils.hasText(borrowItem.getUrl())) {
claimsSetBuilder.claim("url", borrowItem.getUrl());
}
JwtKey jwtKey = jwtKeyManager.getLatest(JWT_BORROW_KEY_NAME, true);
JWSHeader.Builder headerBuilder = new JWSHeader.Builder(
jwtKeyManager.getJwsAlgorithm(jwtKey));
headerBuilder.keyID(jwtKey.getKeyID());
headerBuilder.type(JOSEObjectType.JWT);
SignedJWT jwt = new SignedJWT(headerBuilder.build(), claimsSetBuilder.build());
jwt.sign(jwtKeyManager.createSigner(jwtKey));
return jwt;
}
/**
* Verify.
*
* @param jwt the jwt
* @return true, if successful
* @throws JOSEException the JOSE exception
* @throws ParseException the parse exception
*/
public boolean verify(SignedJWT jwt) throws JOSEException, ParseException {
if (jwt.getHeader() == null || !StringUtils.hasText(jwt.getHeader().getKeyID())) {
throw new KeySourceException("No KeyID provided!");
}
JwtKey jwtKey = jwtKeyManager.getByKeyID(jwt.getHeader().getKeyID());
if (jwtKey == null) {
throw new KeySourceException("No key found for given KeyID! ("
+ jwt.getHeader().getKeyID()
+ ")");
}
if (jwt.getJWTClaimsSet() == null) {
return false;
}
return jwt.verify(jwtKeyManager.createVerfifier(jwtKey));
}
}

View File

@ -0,0 +1,204 @@
/**
*
*/
package de.bstly.we.borrow.controller;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import de.bstly.we.borrow.businesslogic.BorrowItemManager;
import de.bstly.we.borrow.businesslogic.BorrowPermissions;
import de.bstly.we.borrow.controller.validation.BorrowItemValidator;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowItemAvailability;
import de.bstly.we.businesslogic.PermissionManager;
import de.bstly.we.controller.BaseController;
import de.bstly.we.controller.support.EntityResponseStatusException;
import de.bstly.we.controller.support.RequestBodyErrors;
/**
* The Class BorrowItemController.
*/
@RestController
@RequestMapping("/borrow/items")
public class BorrowItemController extends BaseController {
@Autowired
private BorrowItemManager borrowItemManager;
@Autowired
private PermissionManager permissionManager;
@Autowired
private BorrowItemValidator borrowItemValidator;
/**
* Gets the borrow items.
*
* @param pageParameter the page parameter
* @param sizeParameter the size parameter
* @param sortParameter the sort parameter
* @param descParameter the desc parameter
* @param searchParameter the search parameter
* @return the borrow items
*/
@PreAuthorize("isAuthenticated()")
@GetMapping
public Page<BorrowItem> getBorrowItems(@RequestParam("page") Optional<Integer> pageParameter,
@RequestParam("size") Optional<Integer> sizeParameter,
@RequestParam("sort") Optional<String> sortParameter,
@RequestParam("desc") Optional<Boolean> descParameter,
@RequestParam("search") Optional<String> searchParameter) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_REQUESTS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
Page<BorrowItem> borrowItems = borrowItemManager.get(pageParameter.orElse(0),
sizeParameter.orElse(10), sortParameter.orElse("id"), descParameter.orElse(false),
searchParameter.orElse(null));
for (BorrowItem borrowItem : borrowItems.getContent()) {
if (!borrowItem.getOwner().equals(getCurrentUserId())) {
borrowItem.setEmail(null);
borrowItem.setEmailNotification(null);
}
borrowItemManager.applySlots(borrowItem);
}
return borrowItems;
}
/**
* Gets the owner borrow items.
*
* @param pageParameter the page parameter
* @param sizeParameter the size parameter
* @param sortParameter the sort parameter
* @param descParameter the desc parameter
* @param searchParameter the search parameter
* @return the owner borrow items
*/
@PreAuthorize("isAuthenticated()")
@GetMapping("/mine")
public Page<BorrowItem> getOwnerBorrowItems(
@RequestParam("page") Optional<Integer> pageParameter,
@RequestParam("size") Optional<Integer> sizeParameter,
@RequestParam("sort") Optional<String> sortParameter,
@RequestParam("desc") Optional<Boolean> descParameter,
@RequestParam("search") Optional<String> searchParameter) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_ITEMS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
Page<BorrowItem> borrowItems = borrowItemManager.getForUser(getCurrentUserId(),
pageParameter.orElse(0), sizeParameter.orElse(10), sortParameter.orElse("id"),
descParameter.orElse(false), searchParameter.orElse(null));
for (BorrowItem borrowItem : borrowItems.getContent()) {
if (!borrowItem.getOwner().equals(getCurrentUserId())) {
borrowItem.setEmail(null);
borrowItem.setEmailNotification(null);
}
borrowItemManager.applySlots(borrowItem);
}
return borrowItems;
}
/**
* Delete borrow item.
*
* @param id the id
*/
@PreAuthorize("isAuthenticated()")
@GetMapping("/{id}")
public BorrowItem getBorrowItem(@PathVariable("id") Long id) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_REQUESTS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
BorrowItem borrowItem = borrowItemManager.get(id);
if (borrowItem == null) {
throw new EntityResponseStatusException(HttpStatus.NO_CONTENT);
}
if (!borrowItem.getOwner().equals(getCurrentUserId())) {
borrowItem.setEmail(null);
borrowItem.setEmailNotification(null);
}
borrowItemManager.applySlots(borrowItem);
return borrowItem;
}
/**
* Creates the or update borrow item.
*
* @param borrowItem the borrow item
* @return the borrow item
*/
@PreAuthorize("isAuthenticated()")
@PostMapping
public BorrowItem createOrUpdateBorrowItem(@RequestBody BorrowItem borrowItem) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_ITEMS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
if (borrowItem.getId() != null && !borrowItem.getId().equals(0L)) {
BorrowItem original = borrowItemManager.get(borrowItem.getId());
if (original == null || !original.getOwner().equals(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
}
if (borrowItem.getAvailability() == null) {
borrowItem.setAvailability(BorrowItemAvailability.MANUAL);
}
borrowItem.setOwner(getCurrentUserId());
Errors errors = new RequestBodyErrors(borrowItem);
borrowItemValidator.validate(borrowItem, errors);
if (errors.hasErrors()) {
throw new EntityResponseStatusException(errors.getAllErrors(), HttpStatus.CONFLICT);
}
return borrowItemManager.save(borrowItem);
}
/**
* Delete borrow item.
*
* @param id the id
*/
@PreAuthorize("isAuthenticated()")
@DeleteMapping("/{id}")
public void deleteBorrowItem(@PathVariable("id") Long id) {
BorrowItem borrowItem = borrowItemManager.get(id);
if (borrowItem == null || !borrowItem.getOwner().equals(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
borrowItemManager.delete(borrowItem);
}
}

View File

@ -0,0 +1,300 @@
/**
*
*/
package de.bstly.we.borrow.controller;
import java.text.ParseException;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jwt.SignedJWT;
import de.bstly.we.borrow.businesslogic.BorrowItemManager;
import de.bstly.we.borrow.businesslogic.BorrowPermissions;
import de.bstly.we.borrow.businesslogic.BorrowRequestManager;
import de.bstly.we.borrow.controller.validation.BorrowJwtValidator;
import de.bstly.we.borrow.controller.validation.BorrowRequestValidator;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowRequest;
import de.bstly.we.borrow.model.BorrowRequestStatus;
import de.bstly.we.businesslogic.PermissionManager;
import de.bstly.we.controller.BaseController;
import de.bstly.we.controller.support.EntityResponseStatusException;
import de.bstly.we.controller.support.RequestBodyErrors;
/**
* The Class BorrowRequestController.
*/
@RestController
@RequestMapping("/borrow/requests")
public class BorrowRequestController extends BaseController {
@Autowired
private BorrowRequestManager borrowRequestManager;
@Autowired
private BorrowItemManager borrowItemManager;
@Autowired
private BorrowRequestValidator borrowRequestValidator;
@Autowired
private BorrowJwtValidator borrowJwtValidator;
@Autowired
private PermissionManager permissionManager;
@Value("${bstly.we.jwtBorrowIssuer:}")
private String jwtBorrowIssuer;
/**
* Gets the borrow requests.
*
* @param pageParameter the page parameter
* @param sizeParameter the size parameter
* @param sortParameter the sort parameter
* @param descParameter the desc parameter
* @return the borrow requests
*/
@PreAuthorize("isAuthenticated()")
@GetMapping
public Page<BorrowRequest> getBorrowRequests(
@RequestParam("page") Optional<Integer> pageParameter,
@RequestParam("size") Optional<Integer> sizeParameter,
@RequestParam("sort") Optional<String> sortParameter,
@RequestParam("desc") Optional<Boolean> descParameter) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_REQUESTS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
Page<BorrowRequest> borrowRequests = borrowRequestManager.getForUser(getCurrentUserId(),
pageParameter.orElse(0), sizeParameter.orElse(10), sortParameter.orElse("id"),
descParameter.orElse(false));
for (BorrowRequest borrowRequest : borrowRequests.getContent()) {
BorrowItem borrowItem = borrowItemManager.get(borrowRequest.getItem());
borrowItem.setEmail(null);
borrowItem.setEmailNotification(null);
borrowRequest.setBorrowItem(borrowItem);
}
return borrowRequests;
}
/**
* Gets the owner borrow requests.
*
* @param pageParameter the page parameter
* @param sizeParameter the size parameter
* @param sortParameter the sort parameter
* @param descParameter the desc parameter
* @return the owner borrow requests
*/
@PreAuthorize("isAuthenticated()")
@GetMapping("/mine")
public Page<BorrowRequest> getOwnerBorrowRequests(
@RequestParam("page") Optional<Integer> pageParameter,
@RequestParam("size") Optional<Integer> sizeParameter,
@RequestParam("sort") Optional<String> sortParameter,
@RequestParam("desc") Optional<Boolean> descParameter) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_ITEMS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
Page<BorrowRequest> borrowRequests = borrowRequestManager.getForOwner(getCurrentUserId(),
pageParameter.orElse(0), sizeParameter.orElse(10), sortParameter.orElse("id"),
descParameter.orElse(false));
for (BorrowRequest borrowRequest : borrowRequests.getContent()) {
BorrowItem borrowItem = borrowItemManager.get(borrowRequest.getItem());
borrowItem.setEmail(null);
borrowItem.setEmailNotification(null);
borrowRequest.setBorrowItem(borrowItem);
}
return borrowRequests;
}
/**
* Creates the or update borrow request.
*
* @param borrowRequest the borrow request
* @return the borrow request
*/
@PreAuthorize("isAuthenticated()")
@PostMapping
public BorrowRequest createOrUpdateBorrowRequest(@RequestBody BorrowRequest borrowRequest) {
if (!permissionManager.hasPermission(getCurrentUserId(), BorrowPermissions.BORROW_REQUESTS)
|| !permissionManager.isFullUser(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
if (borrowRequest.getId() != null && !borrowRequest.getId().equals(0L)) {
BorrowRequest original = borrowRequestManager.get(borrowRequest.getId());
if (original == null || !original.getUser().equals(getCurrentUserId())
|| !original.getStatus().equals(BorrowRequestStatus.PENDING)) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
}
borrowRequest.setUser(getCurrentUserId());
borrowRequest.setStatus(BorrowRequestStatus.PENDING);
borrowRequest.setReply(null);
Errors errors = new RequestBodyErrors(borrowRequest);
borrowRequestValidator.validate(borrowRequest, errors);
if (errors.hasErrors()) {
throw new EntityResponseStatusException(errors.getAllErrors(), HttpStatus.CONFLICT);
}
BorrowItem borrowItem = borrowItemManager.get(borrowRequest.getItem());
if (borrowItem.isAutoAccept()) {
borrowRequest.setStatus(BorrowRequestStatus.ACCEPTED);
}
if (borrowRequest.getId() == null || borrowRequest.getId().equals(0L)) {
if (borrowItem.getEmailNotification() != null
&& borrowItem.getEmailNotification().booleanValue()) {
borrowItemManager.notifyOwner(borrowItem);
}
}
return borrowRequestManager.save(borrowRequest);
}
/**
* Delete borrow request.
*
* @param id the id
*/
@PreAuthorize("isAuthenticated()")
@DeleteMapping("/{id}")
public void deleteBorrowRequest(@PathVariable("id") Long id) {
BorrowRequest borrowRequest = borrowRequestManager.get(id);
if (borrowRequest == null || !borrowRequest.getUser().equals(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
borrowRequestManager.delete(borrowRequest);
}
/**
* Sets the status.
*
* @param borrowRequest the borrow request
* @return the borrow request
*/
@PreAuthorize("isAuthenticated()")
@PutMapping
public BorrowRequest setStatus(@RequestBody BorrowRequest borrowRequest) {
BorrowRequest original = borrowRequestManager.get(borrowRequest.getId());
if (original == null || original.getItem() == null || original.getItem().equals(0L)
|| !borrowItemManager.exists(original.getItem())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
BorrowItem borrowItem = borrowItemManager.get(original.getItem());
if (!borrowItem.getOwner().equals(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
if (borrowRequest.getStatus().equals(BorrowRequestStatus.PENDING)) {
throw new EntityResponseStatusException(HttpStatus.CONFLICT);
}
original.setStatus(borrowRequest.getStatus());
original.setReply(borrowRequest.getReply());
return borrowRequestManager.save(original);
}
/**
* Gets the code.
*
* @param id the id
* @param request the request
* @return the code
*/
@PreAuthorize("isAuthenticated()")
@GetMapping("/code/{id}")
public String getCode(@PathVariable("id") Long id, HttpServletRequest request) {
BorrowRequest borrowRequest = borrowRequestManager.get(id);
if (borrowRequest == null || !borrowRequest.getUser().equals(getCurrentUserId())) {
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
}
if (!borrowRequest.getStatus().equals(BorrowRequestStatus.ACCEPTED)) {
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
}
String issuer = jwtBorrowIssuer;
if (!StringUtils.hasText(issuer)) {
issuer = request.getScheme()
+ "://"
+ request.getServerName();
if (request.getServerPort() != 443 && request.getServerPort() != 80) {
issuer += ":"
+ request.getServerPort();
}
}
try {
return borrowRequestManager.createCode(borrowRequest, issuer).serialize();
} catch (JOSEException e) {
e.printStackTrace();
throw new EntityResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* Verfiy.
*
* @param serialized the serialized
* @param request the request
* @param response the response
* @return the object
*/
@PostMapping("verify")
public Object verfiy(@RequestBody String serialized, HttpServletRequest request,
HttpServletResponse response) {
try {
SignedJWT signedJwt = SignedJWT.parse(serialized);
Errors errors = new RequestBodyErrors(signedJwt);
borrowJwtValidator.validate(signedJwt, errors);
if (errors.hasErrors()) {
if (errors.getGlobalErrors().isEmpty()) {
throw new EntityResponseStatusException(errors.getAllErrors(),
HttpStatus.PRECONDITION_FAILED);
} else {
throw new EntityResponseStatusException(HttpStatus.NOT_ACCEPTABLE);
}
}
return signedJwt.getJWTClaimsSet().getClaims();
} catch (ParseException e) {
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
}
}
}

View File

@ -0,0 +1,110 @@
/**
*
*/
package de.bstly.we.borrow.controller.validation;
import org.apache.commons.validator.routines.EmailValidator;
import org.apache.commons.validator.routines.UrlValidator;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowItemManualSlot;
import de.bstly.we.borrow.model.BorrowItemPeriodSlot;
import de.bstly.we.borrow.model.BorrowItemSlot;
/**
* The Class BorrowItemValidator.
*/
@Component
public class BorrowItemValidator implements Validator {
private UrlValidator urlValidator = new UrlValidator(UrlValidator.ALLOW_ALL_SCHEMES);
private EmailValidator emailValidator = EmailValidator.getInstance();
/*
* @see org.springframework.validation.Validator#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(BorrowItem.class);
}
/*
* @see org.springframework.validation.Validator#validate(java.lang.Object,
* org.springframework.validation.Errors)
*/
@Override
public void validate(Object target, Errors errors) {
BorrowItem borrowItem = (BorrowItem) target;
if (!StringUtils.hasText(borrowItem.getName())) {
errors.rejectValue("name", "REQUIRED");
}
if (borrowItem.getAvailability() == null) {
errors.rejectValue("availability", "REQUIRED");
}
if (StringUtils.hasText(borrowItem.getUrl())
&& !urlValidator.isValid(borrowItem.getUrl())) {
errors.rejectValue("url", "INVALID_URL");
}
if (StringUtils.hasText(borrowItem.getEmail())
&& !emailValidator.isValid(borrowItem.getEmail())) {
errors.rejectValue("email", "INVALID_EMAIL");
}
if (borrowItem.getMinDuration() != null && borrowItem.getMaxDuration() != null
&& borrowItem.getMinDuration().compareTo(borrowItem.getMaxDuration()) > 0) {
errors.rejectValue("minDuration", "INVALID");
errors.rejectValue("maxDuration", "INVALID");
}
if (borrowItem.getAvailability() != null && borrowItem.getSlots() != null
&& !borrowItem.getSlots().isEmpty()) {
for (BorrowItemSlot borrowItemSlot : borrowItem.getSlots()) {
switch (borrowItem.getAvailability()) {
case MANUAL:
if (borrowItemSlot instanceof BorrowItemManualSlot) {
BorrowItemManualSlot borrowItemManualSlot = (BorrowItemManualSlot) borrowItemSlot;
if (borrowItemManualSlot.getStart() == null
|| borrowItemManualSlot.getEnd() == null) {
errors.rejectValue("slots", "MISSING_DATES");
} else if (borrowItemManualSlot.getStart()
.isAfter(borrowItemManualSlot.getEnd())) {
errors.rejectValue("slots", "INVALID_DATES");
}
}
break;
case PERIOD:
if (borrowItemSlot instanceof BorrowItemPeriodSlot) {
BorrowItemPeriodSlot borrowItemPeriodSlot = (BorrowItemPeriodSlot) borrowItemSlot;
if (borrowItemPeriodSlot.getStartDay() == null
|| borrowItemPeriodSlot.getStartTime() == null
|| borrowItemPeriodSlot.getEndDay() == null
|| borrowItemPeriodSlot.getEndTime() == null) {
errors.rejectValue("slots", "MISSING_DATES");
} else if (borrowItemPeriodSlot.getStartDay()
.compareTo(borrowItemPeriodSlot.getEndDay()) > 0) {
errors.rejectValue("slots", "INVALID_DAY");
} else if (borrowItemPeriodSlot.getStartDay()
.compareTo(borrowItemPeriodSlot.getEndDay()) == 0
&& borrowItemPeriodSlot.getStartTime()
.isAfter(borrowItemPeriodSlot.getEndTime())) {
errors.rejectValue("slots", "INVALID_TIME");
}
}
break;
default:
break;
}
}
}
}
}

View File

@ -0,0 +1,125 @@
/**
*
*/
package de.bstly.we.borrow.controller.validation;
import java.text.ParseException;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import de.bstly.we.borrow.businesslogic.BorrowItemManager;
import de.bstly.we.borrow.businesslogic.BorrowRequestManager;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowRequestStatus;
import de.bstly.we.businesslogic.UserManager;
/**
* The Class BorrowJwtValidator.
*/
@Component
public class BorrowJwtValidator implements Validator {
@Autowired
private BorrowRequestManager borrowRequestManager;
@Autowired
private BorrowItemManager borrowItemManager;
@Autowired
private UserManager userManager;
/*
* @see org.springframework.validation.Validator#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(SignedJWT.class);
}
/*
* @see org.springframework.validation.Validator#validate(java.lang.Object,
* org.springframework.validation.Errors)
*/
@Override
public void validate(Object target, Errors errors) {
SignedJWT signedJwt = (SignedJWT) target;
JWTClaimsSet claims = null;
try {
if (!borrowRequestManager.verify(signedJwt)) {
errors.reject("INVALID");
return;
}
claims = signedJwt.getJWTClaimsSet();
if (claims == null) {
errors.reject("INVALID");
return;
}
} catch (JOSEException | ParseException e) {
errors.reject("INVALID");
return;
}
if (claims.getNotBeforeTime() == null) {
errors.reject("INVALID");
return;
} else if (claims.getNotBeforeTime().after(new Date())) {
errors.rejectValue("nbf", "UPCOMING");
}
if (claims.getExpirationTime() == null) {
errors.reject("INVALID");
return;
} else if (claims.getExpirationTime().before(new Date())) {
errors.rejectValue("exp", "EXPIRED");
}
try {
if (userManager.getByUsername(claims.getStringClaim("owner")) == null) {
errors.rejectValue("owner", "INVALID");
}
} catch (ParseException e) {
errors.reject("INVALID");
return;
}
try {
if (userManager.getByUsername(claims.getStringClaim("user")) == null) {
errors.rejectValue("user", "INVALID");
}
} catch (ParseException e) {
errors.reject("INVALID");
return;
}
try {
BorrowItem borrowItem = borrowItemManager.get(Long.valueOf(claims.getSubject()));
if (borrowItem == null) {
errors.rejectValue("sub", "INVALID");
} else if (!borrowItem.getName().equals(claims.getStringClaim("name"))) {
errors.rejectValue("name", "INVALID");
}
} catch (ParseException e) {
errors.reject("INVALID");
return;
}
try {
if (!BorrowRequestStatus.valueOf(claims.getStringClaim("status"))
.equals(BorrowRequestStatus.ACCEPTED)) {
errors.rejectValue("status", "INVALID");
}
} catch (ParseException e) {
errors.reject("INVALID");
return;
}
}
}

View File

@ -0,0 +1,172 @@
/**
*
*/
package de.bstly.we.borrow.controller.validation;
import java.time.Duration;
import java.time.OffsetTime;
import java.time.ZoneOffset;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import de.bstly.we.borrow.businesslogic.BorrowItemManager;
import de.bstly.we.borrow.model.BorrowItem;
import de.bstly.we.borrow.model.BorrowItemManualSlot;
import de.bstly.we.borrow.model.BorrowItemPeriodSlot;
import de.bstly.we.borrow.model.BorrowItemSlot;
import de.bstly.we.borrow.model.BorrowRequest;
import de.bstly.we.borrow.model.BorrowRequestStatus;
import de.bstly.we.borrow.model.QBorrowRequest;
import de.bstly.we.borrow.repository.BorrowRequestRepository;
/**
* The Class BorrowRequestValidator.
*/
@Component
public class BorrowRequestValidator implements Validator {
@Autowired
private BorrowItemManager borrowItemManager;
@Autowired
private BorrowRequestRepository borrowRequestRepository;
private QBorrowRequest qBorrowRequest = QBorrowRequest.borrowRequest;
/*
* @see org.springframework.validation.Validator#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(BorrowRequest.class);
}
/*
* @see org.springframework.validation.Validator#validate(java.lang.Object,
* org.springframework.validation.Errors)
*/
@Override
public void validate(Object target, Errors errors) {
BorrowRequest borrowRequest = (BorrowRequest) target;
if (borrowRequest.getItem() == null || borrowRequest.getItem().equals(0L)) {
errors.rejectValue("item", "REQUIRED");
return;
}
BorrowItem borrowItem = borrowItemManager.get(borrowRequest.getItem());
if (borrowItem == null) {
errors.rejectValue("item", "INVALID");
return;
}
if (borrowRequest.getStarts() == null) {
errors.rejectValue("starts", "REQUIRED");
}
if (borrowRequest.getEnds() == null) {
errors.rejectValue("ends", "REQUIRED");
}
// expiry + start
if (borrowRequest.getEnds() != null && borrowRequest.getStarts() != null) {
if (borrowRequest.getStarts().isAfter(borrowRequest.getEnds())
|| borrowRequestRepository.exists(qBorrowRequest.item
.eq(borrowRequest.getItem())
// exlude self
.and(qBorrowRequest.id.ne(
borrowRequest.getId() == null ? -1L : borrowRequest.getId()))
// accepted
.and(qBorrowRequest.status.eq(BorrowRequestStatus.ACCEPTED))
// expires after start
.and(qBorrowRequest.ends.after(borrowRequest.getStarts()))
// start before expires
.and(qBorrowRequest.starts.before(borrowRequest.getEnds())))) {
errors.rejectValue("starts", "ALREADY_USED");
errors.rejectValue("ends", "ALREADY_USED");
} else {
if (borrowItem.getMinDuration() != null && borrowItem.getMinDuration().compareTo(
Duration.between(borrowRequest.getEnds(), borrowRequest.getStarts())) > 0) {
errors.rejectValue("starts", "TOO_SHORT");
errors.rejectValue("ends", "TOO_SHORT");
} else if (borrowItem.getMaxDuration() != null
&& Duration.between(borrowRequest.getEnds(), borrowRequest.getStarts())
.compareTo(borrowItem.getMaxDuration()) > 0) {
errors.rejectValue("starts", "TOO_LONG");
errors.rejectValue("ends", "TOO_LONG");
} else {
boolean validSlot = false;
boolean validStart = false;
boolean validEnd = false;
borrowItemManager.applySlots(borrowItem);
switch (borrowItem.getAvailability()) {
case ALWAYS:
validSlot = true;
break;
case MANUAL:
for (BorrowItemSlot borrowItemSlot : borrowItem.getSlots()) {
if (borrowItemSlot instanceof BorrowItemManualSlot) {
BorrowItemManualSlot borrowItemManualSlot = (BorrowItemManualSlot) borrowItemSlot;
if (borrowRequest.getStarts()
.compareTo(borrowItemManualSlot.getStart()) >= 0) {
validStart = true;
}
if (borrowRequest.getEnds()
.compareTo(borrowItemManualSlot.getEnd()) <= 0) {
validEnd = true;
}
if (validStart && validEnd) {
validSlot = true;
break;
}
}
}
break;
case PERIOD:
for (BorrowItemSlot borrowItemSlot : borrowItem.getSlots()) {
if (borrowItemSlot instanceof BorrowItemPeriodSlot) {
BorrowItemPeriodSlot borrowItemPeriodSlot = (BorrowItemPeriodSlot) borrowItemSlot;
if (borrowRequest.getStarts().atZone(ZoneOffset.UTC).getDayOfWeek()
.compareTo(borrowItemPeriodSlot.getStartDay()) >= 0
&& OffsetTime
.ofInstant(borrowRequest.getStarts(),
ZoneOffset.UTC)
.compareTo(
borrowItemPeriodSlot.getStartTime()) >= 0) {
validStart = true;
}
if (borrowRequest.getEnds().atZone(ZoneOffset.UTC).getDayOfWeek()
.compareTo(borrowItemPeriodSlot.getEndDay()) <= 0
&& OffsetTime
.ofInstant(borrowRequest.getEnds(), ZoneOffset.UTC)
.compareTo(
borrowItemPeriodSlot.getEndTime()) <= 0) {
validEnd = true;
}
if (validStart && validEnd) {
validSlot = true;
break;
}
}
}
break;
default:
break;
}
if (!validSlot) {
if (!validStart) {
errors.rejectValue("starts", "INVALID");
}
if (!validEnd) {
errors.rejectValue("ends", "INVALID");
}
}
}
}
}
}
}

View File

@ -0,0 +1,267 @@
/**
*
*/
package de.bstly.we.borrow.model;
import java.time.Duration;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* The Class BorrowItem.
*/
@Entity
@Table(name = "borrow_items")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class BorrowItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "owner", nullable = false)
private Long owner;
@Column(name = "name", nullable = false)
private String name;
@Lob
@Column(name = "description", nullable = true)
private String description;
@Column(name = "url", nullable = true)
private String url;
@Column(name = "email_notification", columnDefinition = "boolean default false")
private Boolean emailNotification;
@Column(name = "email", nullable = true)
private String email;
@Column(name = "auto_acceppt", columnDefinition = "boolean default false")
private boolean autoAccept;
@Column(name = "min_duration", nullable = true)
private Duration minDuration;
@Column(name = "max_duration", nullable = true)
private Duration maxDuration;
@Enumerated(EnumType.STRING)
@Column(name = "availability", nullable = false)
private BorrowItemAvailability availability = BorrowItemAvailability.ALWAYS;
@Transient
private List<? extends BorrowItemSlot> slots;
/**
* Gets the id.
*
* @return the id
*/
public Long getId() {
return id;
}
/**
* Sets the id.
*
* @param id the new id
*/
public void setId(Long id) {
this.id = id;
}
/**
* Gets the owner.
*
* @return the owner
*/
public Long getOwner() {
return owner;
}
/**
* Sets the owner.
*
* @param owner the new owner
*/
public void setOwner(Long owner) {
this.owner = owner;
}
/**
* Gets the name.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets the name.
*
* @param name the new name
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the description.
*
* @return the description
*/
public String getDescription() {
return description;
}
/**
* Sets the description.
*
* @param description the new description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Gets the url.
*
* @return the url
*/
public String getUrl() {
return url;
}
/**
* Sets the url.
*
* @param url the new url
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Gets the email notification.
*
* @return the email notification
*/
public Boolean getEmailNotification() {
return emailNotification;
}
/**
* Sets the email notification.
*
* @param emailNotification the new email notification
*/
public void setEmailNotification(Boolean emailNotification) {
this.emailNotification = emailNotification;
}
/**
* Gets the email.
*
* @return the email
*/
public String getEmail() {
return email;
}
/**
* Sets the email.
*
* @param email the new email
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Checks if is auto accept.
*
* @return true, if is auto accept
*/
public boolean isAutoAccept() {
return autoAccept;
}
/**
* Sets the auto accept.
*
* @param autoAccept the new auto accept
*/
public void setAutoAccept(boolean autoAccept) {
this.autoAccept = autoAccept;
}
/**
* Gets the min duration.
*
* @return the min duration
*/
public Duration getMinDuration() {
return minDuration;
}
/**
* Sets the min duration.
*
* @param minDuration the new min duration
*/
public void setMinDuration(Duration minDuration) {
this.minDuration = minDuration;
}
/**
* Gets the max duration.
*
* @return the max duration
*/
public Duration getMaxDuration() {
return maxDuration;
}
/**
* Sets the max duration.
*
* @param maxDuration the new max duration
*/
public void setMaxDuration(Duration maxDuration) {
this.maxDuration = maxDuration;
}
/**
* @return the availability
*/
public BorrowItemAvailability getAvailability() {
return availability;
}
/**
* @param availability the availability to set
*/
public void setAvailability(BorrowItemAvailability availability) {
this.availability = availability;
}
/**
* @return the slots
*/
public List<? extends BorrowItemSlot> getSlots() {
return slots;
}
/**
* @param slots the slots to set
*/
public void setSlots(List<? extends BorrowItemSlot> slots) {
this.slots = slots;
}
}

View File

@ -0,0 +1,13 @@
/**
*
*/
package de.bstly.we.borrow.model;
/**
* The Enum BorrowItemAvailability.
*/
public enum BorrowItemAvailability {
ALWAYS, PERIOD, MANUAL
}

View File

@ -0,0 +1,52 @@
/**
*
*/
package de.bstly.we.borrow.model;
import java.time.Instant;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* The Class BorrowItemManualSlot.
*/
@Entity
@Table(name = "borrow_item_manual_slots")
public class BorrowItemManualSlot extends BorrowItemSlot {
@Column(name = "start")
private Instant start;
@Column(name = "end")
private Instant end;
/**
* @return the start
*/
public Instant getStart() {
return start;
}
/**
* @param start the start to set
*/
public void setStart(Instant start) {
this.start = start;
}
/**
* @return the end
*/
public Instant getEnd() {
return end;
}
/**
* @param end the end to set
*/
public void setEnd(Instant end) {
this.end = end;
}
}

View File

@ -0,0 +1,85 @@
/**
*
*/
package de.bstly.we.borrow.model;
import java.time.DayOfWeek;
import java.time.OffsetTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* The Class BorrowItemPeriodSlot.
*/
@Entity
@Table(name = "borrow_item_period_slots")
public class BorrowItemPeriodSlot extends BorrowItemSlot {
@Column(name = "start_day")
private DayOfWeek startDay;
@Column(name = "end_day")
private DayOfWeek endDay;
@Column(name = "start_time")
private OffsetTime startTime;
@Column(name = "end_time")
private OffsetTime endTime;
/**
* @return the startDay
*/
public DayOfWeek getStartDay() {
return startDay;
}
/**
* @param startDay the startDay to set
*/
public void setStartDay(DayOfWeek startDay) {
this.startDay = startDay;
}
/**
* @return the endDay
*/
public DayOfWeek getEndDay() {
return endDay;
}
/**
* @param endDay the endDay to set
*/
public void setEndDay(DayOfWeek endDay) {
this.endDay = endDay;
}
/**
* @return the startTime
*/
public OffsetTime getStartTime() {
return startTime;
}
/**
* @param startTime the startTime to set
*/
public void setStartTime(OffsetTime startTime) {
this.startTime = startTime;
}
/**
* @return the endTime
*/
public OffsetTime getEndTime() {
return endTime;
}
/**
* @param endTime the endTime to set
*/
public void setEndTime(OffsetTime endTime) {
this.endTime = endTime;
}
}

View File

@ -0,0 +1,66 @@
/**
*
*/
package de.bstly.we.borrow.model;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
/**
* The Class BorrowItemSlot.
*/
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(value = BorrowItemManualSlot.class, name = "MANUAL"),
@JsonSubTypes.Type(value = BorrowItemPeriodSlot.class, name = "PERIOD") })
public abstract class BorrowItemSlot {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id")
private Long id;
@Column(name = "item")
private Long item;
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the item
*/
public Long getItem() {
return item;
}
/**
* @param item the item to set
*/
public void setItem(Long item) {
this.item = item;
}
}

View File

@ -0,0 +1,212 @@
/**
*
*/
package de.bstly.we.borrow.model;
import java.time.Instant;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Transient;
/**
* The Class BorrowRequest.
*/
@Entity
@Table(name = "borrow_requests")
public class BorrowRequest {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "item", nullable = false)
private Long item;
@Column(name = "user", nullable = false)
private Long user;
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private BorrowRequestStatus status;
@Column(name = "starts", nullable = false)
private Instant starts;
@Column(name = "ends", nullable = false)
private Instant ends;
@Lob
@Column(name = "reply", nullable = true)
private String reply;
@Lob
@Column(name = "comment", nullable = true)
private String comment;
@Transient
private BorrowItem borrowItem;
/**
* Gets the id.
*
* @return the id
*/
public Long getId() {
return id;
}
/**
* Sets the id.
*
* @param id the new id
*/
public void setId(Long id) {
this.id = id;
}
/**
* Gets the item.
*
* @return the item
*/
public Long getItem() {
return item;
}
/**
* Sets the item.
*
* @param item the new item
*/
public void setItem(Long item) {
this.item = item;
}
/**
* Gets the user.
*
* @return the user
*/
public Long getUser() {
return user;
}
/**
* Sets the user.
*
* @param user the new user
*/
public void setUser(Long user) {
this.user = user;
}
/**
* Gets the status.
*
* @return the status
*/
public BorrowRequestStatus getStatus() {
return status;
}
/**
* Sets the status.
*
* @param status the new status
*/
public void setStatus(BorrowRequestStatus status) {
this.status = status;
}
/**
* Gets the starts.
*
* @return the starts
*/
public Instant getStarts() {
return starts;
}
/**
* Sets the starts.
*
* @param starts the new starts
*/
public void setStarts(Instant starts) {
this.starts = starts;
}
/**
* Gets the ends.
*
* @return the ends
*/
public Instant getEnds() {
return ends;
}
/**
* Sets the ends.
*
* @param ends the new ends
*/
public void setEnds(Instant ends) {
this.ends = ends;
}
/**
* Gets the reply.
*
* @return the reply
*/
public String getReply() {
return reply;
}
/**
* Sets the reply.
*
* @param reply the new reply
*/
public void setReply(String reply) {
this.reply = reply;
}
/**
* Gets the comment.
*
* @return the comment
*/
public String getComment() {
return comment;
}
/**
* Sets the comment.
*
* @param comment the new comment
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* Gets the borrow item.
*
* @return the borrow item
*/
public BorrowItem getBorrowItem() {
return borrowItem;
}
/**
* Sets the borrow item.
*
* @param borrowItem the new borrow item
*/
public void setBorrowItem(BorrowItem borrowItem) {
this.borrowItem = borrowItem;
}
}

View File

@ -0,0 +1,12 @@
/**
*
*/
package de.bstly.we.borrow.model;
/**
* The Enum BorrowRequestStatus.
*/
public enum BorrowRequestStatus {
PENDING, ACCEPTED, DENIED
}

View File

@ -0,0 +1,18 @@
/**
*
*/
package de.bstly.we.borrow.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import de.bstly.we.borrow.model.BorrowItemManualSlot;
/**
* The Interface BorrowItemRepository.
*/
@Repository
public interface BorrowItemManualSlotRepository extends JpaRepository<BorrowItemManualSlot, Long>,
QuerydslPredicateExecutor<BorrowItemManualSlot> {
}

View File

@ -0,0 +1,18 @@
/**
*
*/
package de.bstly.we.borrow.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import de.bstly.we.borrow.model.BorrowItemPeriodSlot;
/**
* The Interface BorrowItemRepository.
*/
@Repository
public interface BorrowItemPeriodSlotRepository extends JpaRepository<BorrowItemPeriodSlot, Long>,
QuerydslPredicateExecutor<BorrowItemPeriodSlot> {
}

View File

@ -0,0 +1,18 @@
/**
*
*/
package de.bstly.we.borrow.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import de.bstly.we.borrow.model.BorrowItem;
/**
* The Interface BorrowItemRepository.
*/
@Repository
public interface BorrowItemRepository
extends JpaRepository<BorrowItem, Long>, QuerydslPredicateExecutor<BorrowItem> {
}

View File

@ -0,0 +1,18 @@
/**
*
*/
package de.bstly.we.borrow.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import de.bstly.we.borrow.model.BorrowItemSlot;
/**
* The Interface BorrowItemRepository.
*/
@NoRepositoryBean
public interface BorrowItemSlotRepository
extends JpaRepository<BorrowItemSlot, Long>, QuerydslPredicateExecutor<BorrowItemSlot> {
}

View File

@ -0,0 +1,46 @@
/**
*
*/
package de.bstly.we.borrow.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import de.bstly.we.borrow.model.BorrowRequest;
import de.bstly.we.borrow.model.BorrowRequestStatus;
/**
* The Interface BorrowRequestRepository.
*/
@Repository
public interface BorrowRequestRepository
extends JpaRepository<BorrowRequest, Long>, QuerydslPredicateExecutor<BorrowRequest> {
/**
* Find all by owner.
*
* @param owner the owner
* @param pageable the pageable
* @return the page
*/
@Query("SELECT request FROM BorrowRequest request INNER JOIN BorrowItem item ON request.item = item.id WHERE item.owner = :owner")
Page<BorrowRequest> findAllByOwner(@Param("owner") Long owner, Pageable pageable);
/**
* Find all by owner and status.
*
* @param owner the owner
* @param status the status
* @param pageable the pageable
* @return the page
*/
@Query("SELECT request FROM BorrowRequest request INNER JOIN BorrowItem as item ON request.item = item.id WHERE item.owner = :owner AND request.status = :status")
Page<BorrowRequest> findAllByOwnerAndStatus(@Param("owner") Long owner,
@Param("status") BorrowRequestStatus status, Pageable pageable);
}

View File

@ -8,6 +8,13 @@ import org.springframework.mail.MailMessage;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import de.bstly.we.businesslogic.UserManager;
import de.bstly.we.businesslogic.UserProfileFieldManager;
import de.bstly.we.businesslogic.UserProfileFields;
import de.bstly.we.model.User;
import de.bstly.we.model.UserProfileField;
/**
* The Class EmailManager.
@ -17,6 +24,10 @@ public class EmailManager {
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private UserManager userManager;
@Autowired
private UserProfileFieldManager userProfileFieldManager;
/**
* Send text.
@ -57,4 +68,28 @@ public class EmailManager {
return message;
}
/**
* Gets the user email.
*
* @param user the user
* @return the user email
*/
public String getUserEmail(User user) {
String email = userManager.getBstlyEmail(user.getUsername());
UserProfileField primaryEmailUserProfileField = userProfileFieldManager.get(user.getId(),
UserProfileFields.PROFILE_FIELD_EMAIL_PRIMARY);
if (primaryEmailUserProfileField != null
&& "true".equals(primaryEmailUserProfileField.getValue())) {
UserProfileField emailUserProfileField = userProfileFieldManager.get(user.getId(),
UserProfileFields.PROFILE_FIELD_EMAIL);
if (emailUserProfileField != null
&& StringUtils.hasText(emailUserProfileField.getValue())) {
email = emailUserProfileField.getValue();
}
}
return email;
}
}

View File

@ -74,19 +74,7 @@ public class EmailController extends BaseController {
continue;
}
String email = userManager.getBstlyEmail(user.getUsername());
UserProfileField primaryEmailUserProfileField = userProfileFieldManager
.get(user.getId(), UserProfileFields.PROFILE_FIELD_EMAIL_PRIMARY);
if (primaryEmailUserProfileField != null
&& "true".equals(primaryEmailUserProfileField.getValue())) {
UserProfileField emailUserProfileField = userProfileFieldManager.get(user.getId(),
UserProfileFields.PROFILE_FIELD_EMAIL);
if (emailUserProfileField != null
&& StringUtils.hasText(emailUserProfileField.getValue())) {
email = emailUserProfileField.getValue();
}
}
String email = emailManager.getUserEmail(user);
UserMailModel userMailModel = new UserMailModel(user.getUsername(), email);
@ -95,7 +83,6 @@ public class EmailController extends BaseController {
if (localeUserProfileField != null
&& StringUtils.hasText(localeUserProfileField.getValue())) {
userMailModel.setLocale(localeUserProfileField.getValue());
}
result.add(userMailModel);
@ -104,4 +91,6 @@ public class EmailController extends BaseController {
return result;
}
}

View File

@ -34,7 +34,6 @@ public class JitsiRoomValidator implements Validator {
private JitsiRoomRepository jitsiRoomRepository;
private QJitsiRoom qJitsiRoom = QJitsiRoom.jitsiRoom;
/*
* @see org.springframework.validation.Validator#supports(java.lang.Class)
*/
@ -43,9 +42,9 @@ public class JitsiRoomValidator implements Validator {
return clazz.isAssignableFrom(JitsiRoom.class);
}
/*
* @see org.springframework.validation.Validator#validate(java.lang.Object, org.springframework.validation.Errors)
* @see org.springframework.validation.Validator#validate(java.lang.Object,
* org.springframework.validation.Errors)
*/
@Override
public void validate(Object target, Errors errors) {

26
jwt/pom.xml Normal file
View File

@ -0,0 +1,26 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-main</artifactId>
<version>${revision}</version>
</parent>
<name>jwt</name>
<artifactId>webstly-jwt</artifactId>
<dependencies>
<dependency>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-core</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,293 @@
/**
*
*/
package de.bstly.we.jwt.businesslogic;
import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.UUID;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.google.common.collect.Lists;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.ECDSAVerifier;
import com.nimbusds.jose.crypto.Ed25519Signer;
import com.nimbusds.jose.crypto.Ed25519Verifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.KeyType;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.OctetKeyPair;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import de.bstly.we.jwt.model.JwtKey;
import de.bstly.we.jwt.model.QJwtKey;
import de.bstly.we.jwt.repository.JwtKeyRepository;
/**
* The Class JwtKeyManager.
*/
@Component
public class JwtKeyManager {
@Autowired
private JwtKeyRepository jwtKeyRepository;
private QJwtKey qJwtKey = QJwtKey.jwtKey;
/**
* Gets the jwk set.
*
* @param name the name
* @param createDefault the create default
* @return the jwk set
*/
@Transactional
public JWKSet getJwkSet(String name, boolean createDefault) {
List<JWK> jwkList = Lists.newArrayList();
for (JwtKey jwtKey : jwtKeyRepository.findAll(qJwtKey.name.eq(name))) {
JWK jwk = parseKey(jwtKey);
if (jwk != null) {
jwkList.add(jwk.toPublicJWK());
}
}
if (jwkList.isEmpty()) {
JwtKey jwtKey = getLatest(name, createDefault);
if (jwtKey == null) {
// ERROR
}
JWK jwk = parseKey(jwtKey);
if (jwk != null) {
jwkList.add(jwk.toPublicJWK());
}
}
return new JWKSet(jwkList);
}
/**
* Gets the latest.
*
* @param name the name
* @param createDefault the create default
* @return the latest
*/
@Transactional
public JwtKey getLatest(String name, boolean createDefault) {
List<JwtKey> jwtKeys = Lists.newArrayList(jwtKeyRepository.findAll(qJwtKey.name.eq(name),
PageRequest.of(0, 1, Sort.by(Order.desc("created")))));
JwtKey jwtKey = null;
if (!jwtKeys.isEmpty()) {
jwtKey = jwtKeys.get(0);
}
if (jwtKey != null) {
if (jwtKey.getLifetime() == -1L || jwtKey.getExpires()
.isAfter(Instant.now().plus(jwtKey.getLifetime(), ChronoUnit.SECONDS))) {
return jwtKey;
}
JwtKey newKey = new JwtKey();
newKey.setName(name);
newKey.setKeyParameter(jwtKey.getKeyParameter());
newKey.setKeyType(jwtKey.getKeyType());
newKey.setKeyUse(jwtKey.getKeyUse());
newKey.setLifetime(jwtKey.getLifetime());
newKey.setExpires(Instant.now().plus(jwtKey.getLifetime(), ChronoUnit.SECONDS));
return createKey(newKey);
} else if (createDefault) {
jwtKey = new JwtKey();
jwtKey.setName(name);
return createKey(jwtKey);
}
return null;
}
/**
* Gets the by key ID.
*
* @param keyID the key ID
* @return the by key ID
*/
@Transactional
public JwtKey getByKeyID(String keyID) {
return jwtKeyRepository.findOne(qJwtKey.keyID.eq(keyID)).orElse(null);
}
@Transactional
public JwtKey getByCreated(String name, Instant created) {
return jwtKeyRepository
.findOne(qJwtKey.name.eq(name)
.and(qJwtKey.created.before(created)
.and(qJwtKey.lifetime.eq(-1L).or(qJwtKey.expires.after(created)))))
.orElse(null);
}
/**
* Creates the key.
*
* @param jwtKey the jwt key
* @return the jwt key
*/
public JwtKey createKey(JwtKey jwtKey) {
JWK jwk = null;
String keyID = UUID.randomUUID().toString();
while (jwtKeyRepository.exists(qJwtKey.keyID.eq(keyID))) {
keyID = UUID.randomUUID().toString();
}
try {
KeyType keyType = KeyType.parse(jwtKey.getKeyType());
if (KeyType.EC.equals(keyType)) {
jwk = new ECKeyGenerator(Curve.parse(jwtKey.getKeyParameter()))
.keyUse(KeyUse.parse(jwtKey.getKeyUse())).keyID(keyID).generate();
} else if (KeyType.OCT.equals(keyType)) {
jwk = new OctetKeyPairGenerator(Curve.parse(jwtKey.getKeyParameter()))
.keyUse(KeyUse.parse(jwtKey.getKeyUse())).keyID(keyID).generate();
} else if (KeyType.RSA.equals(keyType)) {
jwk = new RSAKeyGenerator(Integer.valueOf(jwtKey.getKeyParameter()))
.keyUse(KeyUse.parse(jwtKey.getKeyUse())).keyID(keyID).generate();
}
} catch (JOSEException | ParseException e) {
e.printStackTrace();
}
if (jwk != null) {
jwtKey.setKeyID(keyID);
jwtKey.setData(jwk.toJSONString());
jwtKey.setCreated(Instant.now());
jwtKey.setExpires(Instant.now().plus(jwtKey.getLifetime(), ChronoUnit.SECONDS));
return jwtKeyRepository.save(jwtKey);
}
jwtKeyRepository.delete(jwtKey);
return null;
}
/**
* Parses the key.
*
* @param jwtKey the jwt key
* @return the jwk
*/
public JWK parseKey(JwtKey jwtKey) {
try {
KeyType keyType = KeyType.parse(jwtKey.getKeyType());
if (KeyType.EC.equals(keyType)) {
return ECKey.parse(jwtKey.getData());
} else if (KeyType.OCT.equals(keyType)) {
return OctetKeyPair.parse(jwtKey.getData());
} else if (KeyType.RSA.equals(keyType)) {
return RSAKey.parse(jwtKey.getData());
}
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* Gets the jws algorithm.
*
* @param jwtKey the jwt key
* @return the jws algorithm
*/
public JWSAlgorithm getJwsAlgorithm(JwtKey jwtKey) {
if (jwtKey != null && StringUtils.hasText(jwtKey.getJwsAlgorithm())) {
return JWSAlgorithm.parse(jwtKey.getJwsAlgorithm());
}
return null;
}
/**
* Gets the jws algorithm.
*
* @param name the name
* @param createDefault the create default
* @return the jws algorithm
*/
public JWSAlgorithm getJwsAlgorithm(String name, boolean createDefault) {
return getJwsAlgorithm(getLatest(name, createDefault));
}
/**
* Creates the signer.
*
* @param jwtKey the jwt key
* @return the JWS signer
*/
public JWSSigner createSigner(JwtKey jwtKey) {
JWK jwk = parseKey(jwtKey);
if (jwk != null) {
try {
KeyType keyType = KeyType.parse(jwtKey.getKeyType());
if (KeyType.EC.equals(keyType)) {
return new ECDSASigner((ECKey) jwk);
} else if (KeyType.OCT.equals(keyType)) {
return new Ed25519Signer((OctetKeyPair) jwk);
} else if (KeyType.RSA.equals(keyType)) {
return new RSASSASigner((RSAKey) jwk);
}
} catch (JOSEException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Creates the verfifier.
*
* @param jwtKey the jwt key
* @return the JWS verifier
*/
public JWSVerifier createVerfifier(JwtKey jwtKey) {
JWK jwk = parseKey(jwtKey);
if (jwk != null) {
try {
KeyType keyType = KeyType.parse(jwtKey.getKeyType());
if (KeyType.EC.equals(keyType)) {
return new ECDSAVerifier((ECKey) jwk);
} else if (KeyType.OCT.equals(keyType)) {
return new Ed25519Verifier((OctetKeyPair) jwk);
} else if (KeyType.RSA.equals(keyType)) {
return new RSASSAVerifier((RSAKey) jwk);
}
} catch (JOSEException e) {
e.printStackTrace();
}
}
return null;
}
}

View File

@ -0,0 +1,88 @@
/**
*
*/
package de.bstly.we.jwt.businesslogic;
import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.KeySourceException;
import com.nimbusds.jwt.JWTClaimsSet.Builder;
import com.nimbusds.jwt.SignedJWT;
import de.bstly.we.businesslogic.UserManager;
import de.bstly.we.jwt.model.JwtKey;
import de.bstly.we.model.User;
/**
* @author Lurkars
*
*/
@Component
public class JwtUserManager {
@Autowired
private JwtKeyManager jwtKeyManager;
@Autowired
private UserManager userManager;
public static final String JWT_USER_DATA_KEY_NAME = "user";
public SignedJWT createSignedJwt(Long userId, Object data, String issuer, Long lifetime)
throws JOSEException {
User user = userManager.get(userId);
if (user == null) {
return null;
}
Builder claimsSetBuilder = new Builder();
claimsSetBuilder.subject(String.valueOf(user.getId()));
claimsSetBuilder.claim("name", user.getUsername());
claimsSetBuilder.issuer(issuer);
claimsSetBuilder.issueTime(new Date());
if (lifetime != null && lifetime > 0) {
claimsSetBuilder
.expirationTime(Date.from(Instant.now().plus(lifetime, ChronoUnit.SECONDS)));
}
if (data != null) {
claimsSetBuilder.claim("data", data);
}
JwtKey jwtKey = jwtKeyManager.getLatest(JWT_USER_DATA_KEY_NAME, true);
JWSHeader.Builder headerBuilder = new JWSHeader.Builder(
jwtKeyManager.getJwsAlgorithm(jwtKey));
headerBuilder.keyID(jwtKey.getKeyID());
headerBuilder.type(JOSEObjectType.JWT);
SignedJWT jwt = new SignedJWT(headerBuilder.build(), claimsSetBuilder.build());
jwt.sign(jwtKeyManager.createSigner(jwtKey));
return jwt;
}
public boolean verify(SignedJWT jwt) throws JOSEException, ParseException {
if (jwt.getHeader() == null || !StringUtils.hasText(jwt.getHeader().getKeyID())) {
throw new KeySourceException("No KeyID provided!");
}
JwtKey jwtKey = jwtKeyManager.getByKeyID(jwt.getHeader().getKeyID());
if (jwtKey == null) {
throw new KeySourceException("No key found for given keyID!");
}
return jwt.verify(jwtKeyManager.createVerfifier(jwtKey));
}
}

View File

@ -0,0 +1,105 @@
/**
*
*/
package de.bstly.we.jwt.controller;
import java.text.ParseException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jwt.SignedJWT;
import de.bstly.we.controller.BaseController;
import de.bstly.we.controller.support.EntityResponseStatusException;
import de.bstly.we.jwt.businesslogic.JwtUserManager;
/**
* @author Lurkars
*
*/
@RestController
@RequestMapping("/jwt/user")
public class JwtUserDataController extends BaseController {
@Autowired
private JwtUserManager jwtUserDataManager;
@Value("${bstly.we.jwtUserIssuer:}")
private String jwtUserIssuer;
@PreAuthorize("isAuthenticated()")
@GetMapping("/auth")
public String createSignedJwtAuth(HttpServletRequest request) {
String issuer = jwtUserIssuer;
if (!StringUtils.hasText(issuer)) {
issuer = request.getScheme()
+ "://"
+ request.getServerName();
if (request.getServerPort() != 443 && request.getServerPort() != 80) {
issuer += ":"
+ request.getServerPort();
}
}
try {
return jwtUserDataManager.createSignedJwt(getCurrentUserId(), null, issuer, 120L)
.serialize();
} catch (JOSEException e) {
e.printStackTrace();
throw new EntityResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PreAuthorize("isAuthenticated()")
@PostMapping("/create")
public String createSignedJwt(@RequestBody Object data, HttpServletRequest request) {
String issuer = jwtUserIssuer;
if (!StringUtils.hasText(issuer)) {
issuer = request.getScheme()
+ "://"
+ request.getServerName();
if (request.getServerPort() != 443 && request.getServerPort() != 80) {
issuer += ":"
+ request.getServerPort();
}
}
try {
return jwtUserDataManager.createSignedJwt(getCurrentUserId(), data, issuer, -1L)
.serialize();
} catch (JOSEException e) {
e.printStackTrace();
throw new EntityResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("verify")
public Object verfiy(@RequestBody String serialized, HttpServletRequest request,
HttpServletResponse response) {
try {
SignedJWT signedJwt = SignedJWT.parse(serialized);
if (jwtUserDataManager.verify(signedJwt)) {
return signedJwt.getJWTClaimsSet();
} else {
throw new EntityResponseStatusException(HttpStatus.NOT_ACCEPTABLE);
}
} catch (ParseException e) {
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
} catch (JOSEException e) {
e.printStackTrace();
throw new EntityResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -0,0 +1,252 @@
/**
*
*/
package de.bstly.we.jwt.model;
import java.time.Instant;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.KeyType;
import com.nimbusds.jose.jwk.KeyUse;
/**
* The Class JwtKey.
*/
@Entity
@Table(name = "jwt_keys")
public class JwtKey {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "key_id", nullable = true)
private String keyID;
@Lob
@Column(name = "data", nullable = true)
private String data;
@Column(name = "key_type", nullable = false)
private String keyType = KeyType.EC.getValue();
@Column(name = "key_parameter", nullable = false)
private String keyParameter = Curve.P_256.toString();
@Column(name = "key_use", nullable = false)
private String keyUse = KeyUse.SIGNATURE.getValue();
@Column(name = "jws_algorithm", nullable = false)
private String jwsAlgorithm = JWSAlgorithm.ES256.getName();
@Column(name = "lifetime", nullable = false)
private Long lifetime = -1L;
@Column(name = "created", nullable = false)
private Instant created;
@Column(name = "expires", nullable = false)
private Instant expires;
/**
* Gets the id.
*
* @return the id
*/
public Long getId() {
return id;
}
/**
* Sets the id.
*
* @param id the new id
*/
public void setId(Long id) {
this.id = id;
}
/**
* Gets the name.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets the name.
*
* @param name the new name
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the key ID.
*
* @return the key ID
*/
public String getKeyID() {
return keyID;
}
/**
* Sets the key ID.
*
* @param keyID the new key ID
*/
public void setKeyID(String keyID) {
this.keyID = keyID;
}
/**
* Gets the data.
*
* @return the data
*/
public String getData() {
return data;
}
/**
* Sets the data.
*
* @param data the new data
*/
public void setData(String data) {
this.data = data;
}
/**
* Gets the key type.
*
* @return the key type
*/
public String getKeyType() {
return keyType;
}
/**
* Sets the key type.
*
* @param keyType the new key type
*/
public void setKeyType(String keyType) {
this.keyType = keyType;
}
/**
* Gets the key parameter.
*
* @return the key parameter
*/
public String getKeyParameter() {
return keyParameter;
}
/**
* Sets the key parameter.
*
* @param keyParameter the new key parameter
*/
public void setKeyParameter(String keyParameter) {
this.keyParameter = keyParameter;
}
/**
* Gets the key use.
*
* @return the key use
*/
public String getKeyUse() {
return keyUse;
}
/**
* Sets the key use.
*
* @param keyUse the new key use
*/
public void setKeyUse(String keyUse) {
this.keyUse = keyUse;
}
/**
* Gets the jws algorithm.
*
* @return the jws algorithm
*/
public String getJwsAlgorithm() {
return jwsAlgorithm;
}
/**
* Sets the jws algorithm.
*
* @param jwsAlgorithm the new jws algorithm
*/
public void setJwsAlgorithm(String jwsAlgorithm) {
this.jwsAlgorithm = jwsAlgorithm;
}
/**
* Gets the lifetime.
*
* @return the lifetime
*/
public Long getLifetime() {
return lifetime;
}
/**
* Sets the lifetime.
*
* @param lifetime the new lifetime
*/
public void setLifetime(Long lifetime) {
this.lifetime = lifetime;
}
/**
* Gets the created.
*
* @return the created
*/
public Instant getCreated() {
return created;
}
/**
* Sets the created.
*
* @param created the new created
*/
public void setCreated(Instant created) {
this.created = created;
}
/**
* Gets the expires.
*
* @return the expires
*/
public Instant getExpires() {
return expires;
}
/**
* Sets the expires.
*
* @param expires the new expires
*/
public void setExpires(Instant expires) {
this.expires = expires;
}
}

View File

@ -0,0 +1,18 @@
/**
*
*/
package de.bstly.we.jwt.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import de.bstly.we.jwt.model.JwtKey;
/**
* The Interface JwtKeyRepository.
*/
@Repository
public interface JwtKeyRepository
extends JpaRepository<JwtKey, Long>, QuerydslPredicateExecutor<JwtKey> {
}

View File

@ -14,13 +14,8 @@
<dependencies>
<dependency>
<groupId>de.bstly.we</groupId>
<artifactId>webstly-core</artifactId>
<artifactId>webstly-jwt</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -3,7 +3,6 @@
*/
package de.bstly.we.oidc.businesslogic;
import java.text.ParseException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
@ -11,9 +10,8 @@ import java.util.Date;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
@ -21,24 +19,22 @@ import org.springframework.util.StringUtils;
import com.beust.jcommander.internal.Maps;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import com.nimbusds.jose.jwk.KeyType;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jwt.JWTClaimsSet.Builder;
import com.nimbusds.jwt.SignedJWT;
import de.bstly.we.businesslogic.PermissionManager;
import de.bstly.we.businesslogic.QuotaManager;
import de.bstly.we.businesslogic.SystemPropertyManager;
import de.bstly.we.businesslogic.UserManager;
import de.bstly.we.businesslogic.UserProfileFieldManager;
import de.bstly.we.businesslogic.UserProfileFields;
import de.bstly.we.jwt.businesslogic.JwtKeyManager;
import de.bstly.we.jwt.model.JwtKey;
import de.bstly.we.model.Permission;
import de.bstly.we.model.Quota;
import de.bstly.we.model.User;
@ -52,17 +48,12 @@ import de.bstly.we.oidc.repository.OidcTokenRepository;
* The Class OidcTokenManager.
*/
@Service
public class OidcTokenManager {
public class OidcTokenManager implements SmartInitializingSingleton {
public static final int ACCESS_TOKEN_LENGTH = 64;
public static final String OIDC_JWK_PUBLIC_KEY = "oidc-jwk-public-key";
public static final String OIDC_JWT_KEY_NAME = "oidc";
public static final String BEARER_TOKEN_TYPE = "Bearer";
private RSAKey publicKey;
private JWKSet jwkSet;
private JWSSigner signer;
private JWSVerifier verifier;
@Autowired
private OidcTokenRepository tokenRepository;
@Autowired
@ -74,32 +65,37 @@ public class OidcTokenManager {
@Autowired
private QuotaManager quotaManager;
@Autowired
private SystemPropertyManager systemPropertyManager;
private JwtKeyManager jwtKeyManager;
private QOidcToken qOidcToken = QOidcToken.oidcToken;
/**
* Inits the oidc token manager.
*
* @throws JOSEException the JOSE exception
/*
* @see org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated()
*/
@PostConstruct
public void initOidcTokenManager() throws JOSEException {
RSAKey rsaJWK = null;
if (systemPropertyManager.has(OIDC_JWK_PUBLIC_KEY)) {
try {
rsaJWK = RSAKey.parse(systemPropertyManager.get(OIDC_JWK_PUBLIC_KEY));
} catch (ParseException e) {
/*
* @see org.springframework.beans.factory.SmartInitializingSingleton#
* afterSingletonsInstantiated()
*/
@Override
public void afterSingletonsInstantiated() {
if (jwtKeyManager.getLatest(OIDC_JWT_KEY_NAME, false) == null) {
createDefaultJwtKey();
}
} else {
rsaJWK = new RSAKeyGenerator(2048).keyID("1").generate();
systemPropertyManager.add(OIDC_JWK_PUBLIC_KEY, rsaJWK.toJSONString());
}
this.publicKey = rsaJWK.toPublicJWK();
this.signer = new RSASSASigner(rsaJWK);
this.jwkSet = new JWKSet(this.publicKey);
this.verifier = new RSASSAVerifier(this.publicKey);
/**
* Creates the default jwt key.
*
* @return the jwt key
*/
protected JwtKey createDefaultJwtKey() {
JwtKey jwtKey = new JwtKey();
jwtKey.setName(OIDC_JWT_KEY_NAME);
jwtKey.setKeyType(KeyType.RSA.getValue());
jwtKey.setKeyParameter("2048");
jwtKey.setJwsAlgorithm(JWSAlgorithm.RS256.getName());
jwtKey.setKeyUse(KeyUse.SIGNATURE.getValue());
jwtKey.setLifetime(-1L);
return jwtKeyManager.createKey(jwtKey);
}
/**
@ -154,11 +150,19 @@ public class OidcTokenManager {
claimsSetBuilder.claim("nonce", nonce);
}
JWSHeader.Builder headerBuilder = new JWSHeader.Builder(JWSAlgorithm.RS256);
headerBuilder.keyID(getPublicKey().getKeyID());
JwtKey jwtKey = jwtKeyManager.getLatest(OIDC_JWT_KEY_NAME, false);
if (jwtKey == null) {
jwtKey = createDefaultJwtKey();
}
JWSHeader.Builder headerBuilder = new JWSHeader.Builder(
jwtKeyManager.getJwsAlgorithm(jwtKey));
headerBuilder.keyID(jwtKey.getKeyID());
headerBuilder.type(JOSEObjectType.JWT);
SignedJWT jwt = new SignedJWT(headerBuilder.build(), claimsSetBuilder.build());
jwt.sign(getSigner());
jwt.sign(jwtKeyManager.createSigner(jwtKey));
token.setIdToken(jwt.serialize());
@ -234,39 +238,12 @@ public class OidcTokenManager {
return tokenRepository.findOne(qOidcToken.accessToken.eq(accessToken)).orElse(null);
}
/**
* Gets the signer.
*
* @return the signer
*/
public JWSSigner getSigner() {
return signer;
}
/**
* Gets the verifier.
*
* @return the verifier
*/
public JWSVerifier getVerifier() {
return verifier;
}
/**
* Gets the public key.
*
* @return the public key
*/
public RSAKey getPublicKey() {
return publicKey;
}
/**
* Gets the jwk set.
*
* @return the jwk set
*/
public JWKSet getJwkSet() {
return jwkSet;
return jwtKeyManager.getJwkSet(OIDC_JWT_KEY_NAME, false);
}
}

View File

@ -80,17 +80,19 @@ public class RoomController extends DebugLogger {
memberData.setTags(Lists.newArrayList());
try {
Long userId = Long.parseLong(userIdentifier);
User user = userManager.get(userId);
if (user != null && permissionManager.isFullUser(userId)
&& permissionManager.hasPermission(userId, ParteyPermissions.PARTEY)) {
if (StringUtils.hasText(visitCardUrlFormat)) {
User user = userManager.get(userId);
memberData.setVisitCardUrl(String.format(visitCardUrlFormat, user.getUsername()));
memberData
.setVisitCardUrl(String.format(visitCardUrlFormat, user.getUsername()));
}
if (permissionManager.isFullUser(userId)
&& permissionManager.hasPermission(userId, ParteyPermissions.PARTEY)) {
memberData.setAnonymous(false);
for (ParteyUserTag parteyUserTag : parteyUserTagManager.getForTarget(userId)) {
for (ParteyUserTag parteyUserTag : parteyUserTagManager
.getNonExpiredForUsername(user.getUsername())) {
memberData.getTags().add(parteyUserTag.getTag());
}
}

View File

@ -40,7 +40,7 @@ public class ParteyMapManager {
private String internalMapUri;
private Pattern internalMapUriPattern = Pattern.compile("\\/@\\/(.+)");
private Pattern externalMapUriPattern = Pattern.compile("\\/_\\/(.+)");
private Pattern externalMapUriPattern = Pattern.compile("\\/_\\/[^/]+\\/(.+)");
/**
* Gets the.

View File

@ -3,6 +3,7 @@
*/
package de.bstly.we.partey.businesslogic;
import java.time.Instant;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
@ -10,10 +11,15 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import com.google.common.collect.Lists;
import com.querydsl.core.BooleanBuilder;
import de.bstly.we.businesslogic.UserManager;
import de.bstly.we.model.User;
import de.bstly.we.partey.model.ParteyUserTag;
import de.bstly.we.partey.model.ParteyUserTag.ParteyUserTagId;
import de.bstly.we.partey.model.QParteyUserTag;
import de.bstly.we.partey.repository.ParteyUserTagRepository;
@ -25,18 +31,20 @@ public class ParteyUserTagManager {
@Autowired
private ParteyUserTagRepository parteyUserTagRepository;
@Autowired
private UserManager userManager;
private QParteyUserTag qParteyUserTag = QParteyUserTag.parteyUserTag;
/**
* Gets the.
* Gets the all.
*
* @param page the page
* @param size the size
* @param sortBy the sort by
* @param descending the descending
* @return the page
* @return the all
*/
public Page<ParteyUserTag> get(int page, int size, String sortBy, boolean descending) {
public Page<ParteyUserTag> getAll(int page, int size, String sortBy, boolean descending) {
PageRequest pageRequest = PageRequest.of(page, size,
descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending());
@ -44,14 +52,85 @@ public class ParteyUserTagManager {
}
/**
* Gets the for target.
* Gets the all by user id.
*
* @param target the target
* @return the for target
* @param userId the user id
* @return the all by user id
*/
public List<ParteyUserTag> getForTarget(long target) {
return Lists
.newArrayList(parteyUserTagRepository.findAll(qParteyUserTag.target.eq(target)));
public List<ParteyUserTag> getAllByUserId(Long userId) {
User user = userManager.get(userId);
Assert.notNull(user, "invalid userId: '"
+ userId
+ "'!");
return getAllForUsername(user.getUsername());
}
/**
* Gets the non expired by user id.
*
* @param userId the user id
* @return the non expired by user id
*/
public List<ParteyUserTag> getNonExpiredByUserId(Long userId) {
return getNonExpiredByUserId(userId, false);
}
/**
* Gets the non expired by user id.
*
* @param userId the user id
* @param upcoming the upcoming
* @return the non expired by user id
*/
public List<ParteyUserTag> getNonExpiredByUserId(Long userId, boolean upcoming) {
User user = userManager.get(userId);
Assert.notNull(user, "invalid userId: '"
+ userId
+ "'!");
return getNonExpiredForUsername(user.getUsername(), upcoming);
}
/**
* Gets the all for username.
*
* @param username the username
* @return the all for username
*/
public List<ParteyUserTag> getAllForUsername(String username) {
return Lists.newArrayList(
parteyUserTagRepository.findAll(qParteyUserTag.username.eq(username)));
}
/**
* Gets the non expired for username.
*
* @param username the username
* @return the non expired for username
*/
public List<ParteyUserTag> getNonExpiredForUsername(String username) {
return getNonExpiredForUsername(username, false);
}
/**
* Gets the non expired for username.
*
* @param username the username
* @param upcoming the upcoming
* @return the non expired for username
*/
public List<ParteyUserTag> getNonExpiredForUsername(String username, boolean upcoming) {
BooleanBuilder query = new BooleanBuilder();
query.and(qParteyUserTag.username.eq(username));
query.and(qParteyUserTag.expires.isNull().or(qParteyUserTag.expires.after(Instant.now())));
if (!upcoming) {
query.and(
qParteyUserTag.starts.isNull().or(qParteyUserTag.starts.before(Instant.now())));
}
return Lists.newArrayList(parteyUserTagRepository.findAll(query.getValue()));
}
/**
@ -67,20 +146,21 @@ public class ParteyUserTagManager {
/**
* Delete.
*
* @param id the id
* @param parteyUserTag the partey user tag
*/
public void delete(Long id) {
parteyUserTagRepository.deleteById(id);
public void delete(ParteyUserTag parteyUserTag) {
parteyUserTagRepository.deleteById(
new ParteyUserTagId(parteyUserTag.getUsername(), parteyUserTag.getTag()));
}
/**
* Delete all for target.
*
* @param target the target
* @param username the username
*/
public void deleteAllForTarget(Long target) {
public void deleteAllForTarget(String username) {
parteyUserTagRepository
.deleteAll(parteyUserTagRepository.findAll(qParteyUserTag.target.eq(target)));
.deleteAll(parteyUserTagRepository.findAll(qParteyUserTag.username.eq(username)));
}
}

View File

@ -33,7 +33,7 @@ public class UserTagController extends BaseController {
@PreAuthorize("isAuthenticated()")
@GetMapping
public List<ParteyUserTag> getParteyUserTagsForTarget() {
return parteyUserTagManager.getForTarget(getCurrentUserId());
return parteyUserTagManager.getNonExpiredByUserId(getCurrentUserId(), true);
}
}

View File

@ -55,8 +55,8 @@ public class UserTagManagementController extends BaseController {
@RequestParam("sort") Optional<String> sortParameter,
@RequestParam("desc") Optional<Boolean> descParameter) {
Page<ParteyUserTag> page = parteyUserTagManager.get(pageParameter.orElse(0),
sizeParameter.orElse(10), sortParameter.orElse("id"), descParameter.orElse(false));
Page<ParteyUserTag> page = parteyUserTagManager.getAll(pageParameter.orElse(0),
sizeParameter.orElse(10), sortParameter.orElse("username"), descParameter.orElse(false));
return page;
}
@ -77,7 +77,45 @@ public class UserTagManagementController extends BaseController {
throw new EntityResponseStatusException(HttpStatus.NO_CONTENT);
}
return parteyUserTagManager.getForTarget(user.getId());
return parteyUserTagManager.getAllForUsername(username);
}
/**
* Gets the non expired partey user tags for target.
*
* @param username the username
* @return the non expired partey user tags for target
*/
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("{username}/active")
public List<ParteyUserTag> getNonExpiredParteyUserTagsForTarget(
@PathVariable("username") String username) {
User user = userManager.getByUsername(username);
if (user == null) {
throw new EntityResponseStatusException(HttpStatus.NO_CONTENT);
}
return parteyUserTagManager.getNonExpiredForUsername(username);
}
/**
* Gets the upcoming partey user tags for target.
*
* @param username the username
* @return the upcoming partey user tags for target
*/
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("{username}/upcoming")
public List<ParteyUserTag> getUpcomingParteyUserTagsForTarget(
@PathVariable("username") String username) {
User user = userManager.getByUsername(username);
if (user == null) {
throw new EntityResponseStatusException(HttpStatus.NO_CONTENT);
}
return parteyUserTagManager.getNonExpiredForUsername(username, true);
}
/**
@ -95,12 +133,12 @@ public class UserTagManagementController extends BaseController {
/**
* Delete partey user tag.
*
* @param id the id
* @param parteyUserTag the partey user tag
*/
@PreAuthorize("hasRole('ROLE_ADMIN')")
@DeleteMapping("{id}")
public void deleteParteyUserTag(@PathVariable("id") Long id) {
parteyUserTagManager.delete(id);
@DeleteMapping
public void deleteParteyUserTag(@RequestBody ParteyUserTag parteyUserTag) {
parteyUserTagManager.delete(parteyUserTag);
}
}

View File

@ -3,59 +3,50 @@
*/
package de.bstly.we.partey.model;
import java.io.Serializable;
import java.time.Instant;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import de.bstly.we.partey.model.ParteyUserTag.ParteyUserTagId;
/**
* The Class ParteyUserTag.
*/
@Entity
@IdClass(ParteyUserTagId.class)
@Table(name = "partey_user_tags")
public class ParteyUserTag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
private Long target;
private String username;
@Id
private String tag;
@Column(name = "starts", nullable = true)
private Instant starts;
@Column(name = "expires", nullable = true)
private Instant expires;
/**
* Gets the id.
* Gets the username.
*
* @return the id
* @return the username
*/
public Long getId() {
return id;
public String getUsername() {
return username;
}
/**
* Sets the id.
* Sets the username.
*
* @param id the new id
* @param username the new username
*/
public void setId(Long id) {
this.id = id;
}
/**
* Gets the target.
*
* @return the target
*/
public Long getTarget() {
return target;
}
/**
* Sets the target.
*
* @param target the new target
*/
public void setTarget(Long target) {
this.target = target;
public void setUsername(String username) {
this.username = username;
}
/**
@ -76,4 +67,99 @@ public class ParteyUserTag {
this.tag = tag;
}
/**
* Gets the starts.
*
* @return the starts
*/
public Instant getStarts() {
return starts;
}
/**
* Sets the starts.
*
* @param starts the new starts
*/
public void setStarts(Instant starts) {
this.starts = starts;
}
/**
* Gets the expires.
*
* @return the expires
*/
public Instant getExpires() {
return expires;
}
/**
* Sets the expires.
*
* @param expires the new expires
*/
public void setExpires(Instant expires) {
this.expires = expires;
}
public static class ParteyUserTagId implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String tag;
public ParteyUserTagId() {
super();
}
/**
* @param username
* @param tag
*/
public ParteyUserTagId(String username, String tag) {
super();
this.username = username;
this.tag = tag;
}
/**
* Gets the username.
*
* @return the username
*/
public String getUsername() {
return username;
}
/**
* Sets the username.
*
* @param username the new username
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Gets the tag.
*
* @return the tag
*/
public String getTag() {
return tag;
}
/**
* Sets the tag.
*
* @param tag the new tag
*/
public void setTag(String tag) {
this.tag = tag;
}
}
}

View File

@ -8,11 +8,12 @@ import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
import de.bstly.we.partey.model.ParteyUserTag;
import de.bstly.we.partey.model.ParteyUserTag.ParteyUserTagId;
/**
* The Interface ParteyUserTagRepository.
*/
@Repository
public interface ParteyUserTagRepository
extends JpaRepository<ParteyUserTag, Long>, QuerydslPredicateExecutor<ParteyUserTag> {
public interface ParteyUserTagRepository extends JpaRepository<ParteyUserTag, ParteyUserTagId>,
QuerydslPredicateExecutor<ParteyUserTag> {
}

View File

@ -12,7 +12,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>11</java.version>
<revision>1.1.0-SNAPSHOT</revision>
<revision>1.2.0-SNAPSHOT</revision>
</properties>
<parent>
@ -24,11 +24,13 @@
<modules>
<module>application</module>
<module>borrow</module>
<module>core</module>
<module>email</module>
<module>i18n</module>
<module>invite</module>
<module>jitsi</module>
<module>jwt</module>
<module>membership</module>
<module>minetest</module>
<module>oidc</module>