ClientsController.java
package se.jobtechdev.personaldatagateway.api.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.context.request.NativeWebRequest;
import se.jobtechdev.personaldatagateway.api.generated.api.ClientsApi;
import se.jobtechdev.personaldatagateway.api.generated.model.*;
import se.jobtechdev.personaldatagateway.api.service.ClientService;
import se.jobtechdev.personaldatagateway.api.service.OrganizationService;
import se.jobtechdev.personaldatagateway.api.service.RedirectService;
import se.jobtechdev.personaldatagateway.api.util.*;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import static se.jobtechdev.personaldatagateway.api.util.ControllerUtil.*;
@Controller
public class ClientsController implements ClientsApi {
private final ClientService clientService;
private final RedirectService redirectService;
private final OrganizationService organizationService;
private final NativeWebRequest request;
private final String baseUrl;
@Autowired
public ClientsController(
@Value("${pdg-api.base-url}") String baseUrl,
NativeWebRequest request,
ClientService clientService,
RedirectService redirectService,
OrganizationService organizationService) {
this.baseUrl = baseUrl;
this.request = request;
this.clientService = clientService;
this.redirectService = redirectService;
this.organizationService = organizationService;
}
@Override
public ResponseEntity<Client> postClient(Client client) {
final var organizationId = client.getOrganizationId();
final var organization =
throwApiExceptionOnAbsentValue(
organizationService.getOrganizationById(organizationId),
HttpStatus.BAD_REQUEST,
String.format(
"Failed to handle POST client request - Could not find organization with id:"
+ " %s",
organizationId));
final var clientKey = KeyProvider.randomBytes(32);
final var createdClient =
clientService.createClient(
organization, client.getName(), client.getRole().getValue(), clientKey);
final var savedClient =
ResponseFactory.createClient(createdClient);
final var headers =
LinkHeaderUtil.createHeadersWithLocation(savedClient.getClientId().toString(), new Class<?>[]{Client.class}, client);
return ResponseEntity.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(savedClient);
}
@Override
public ResponseEntity<List<Client>> getClients(Integer offset, Integer limit) {
final var pageable = OffsetBasedPageRequest.of(offset, limit);
final var clients = clientService.getClients(pageable);
final var response =
clients.stream()
.map(ResponseFactory::createClient)
.toList();
final var headers =
LinkHeaderUtil.createHeadersWithPagination(
clients, new Class<?>[]{Integer.class, Integer.class}, (int) pageable.getOffset(), pageable.getPageSize());
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
;
@Override
public ResponseEntity<Client> getClient(UUID clientId) {
final var loggedInClientAndRole = loggedInClientAndRole();
earlyExit(
loggedInClientAndRole.clientId(),
id -> (loggedInClientAndRole.role().equals("consumer") && !id.equals(clientId.toString())),
HttpStatus.UNAUTHORIZED,
String.format(
"Failed to handle GET client request - Requested id: %s does not match logged in client id: %s",
clientId.toString(),
loggedInClientAndRole.clientId()));
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle GET client request - Could not find client with id: %s",
clientId));
final var response = ResponseFactory.createClient(client);
final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{UUID.class}, clientId);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<Client> patchClient(
UUID clientId, ClientPatch clientPatch) {
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle PATCH client request - Could not find client with id: %s",
clientId));
final var assigned = assignNonNull(clientPatch, client, List.of("revoked"));
final var savedClient = clientService.saveClient(assigned);
final var response = ResponseFactory.createClient(savedClient);
final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{UUID.class, ClientPatch.class}, clientId, clientPatch);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<ClientRedirect> postClientRedirect(
UUID clientId, ClientRedirect clientRedirect) {
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle POST client redirect request - Could not find client with id:"
+ " %s",
clientId));
final var redirect =
redirectService.createRedirect(clientRedirect.getRedirectUrl(), client);
final var response = ResponseFactory.createClientRedirect(redirect);
final var headers =
LinkHeaderUtil.createHeadersWithLocation(
redirect.getId().toString(), new Class<?>[]{UUID.class, ClientRedirect.class}, clientId, clientRedirect);
return ResponseEntity.status(HttpStatus.CREATED)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<List<ClientRedirect>> getClientRedirects(
UUID clientId, Integer offset, Integer limit) {
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle GET client redirects request - Could not find client with id:"
+ " %s",
clientId));
final var pageable = OffsetBasedPageRequest.of(offset, limit);
final var redirects = redirectService.getRedirects(client, pageable);
final var response =
redirects.stream()
.map(
ResponseFactory::createClientRedirect)
.toList();
final var headers =
LinkHeaderUtil.createHeadersWithPagination(
redirects, new Class<?>[]{UUID.class, Integer.class, Integer.class}, clientId, (int) pageable.getOffset(), pageable.getPageSize());
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<ClientRedirect> getClientRedirect(
UUID clientId, UUID redirectId) {
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle GET client redirect request - Could not find client with id:"
+ " %s",
clientId));
final var redirect =
throwApiExceptionOnAbsentValue(
redirectService.getRedirectById(redirectId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle GET client redirect request - Could not find redirect with id: %s",
redirectId));
final var redirectClientId = redirect.getClientEntity().getId();
earlyExit(
redirectClientId,
UuidProvider.generateInequalityCheckingLambda(client.getId()),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle GET client redirect request - The redirect's [clientId:%s] does not"
+ " match provided [clientId:%s]",
redirectClientId, clientId));
final var response =
ResponseFactory.createClientRedirect(redirect);
final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{UUID.class, UUID.class}, clientId, redirectId);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<ClientRedirect> patchClientRedirect(
UUID clientId, UUID redirectId, ClientRedirectPatch clientRedirectPatch) {
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle PATCH client redirect request - Could not find client with id: %s",
clientId));
final var redirect =
throwApiExceptionOnAbsentValue(
redirectService.getRedirectById(redirectId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle PATCH client redirect request - Could not find redirect with id: %s",
redirectId));
final var redirectClientId = redirect.getClientEntity().getId();
earlyExit(
redirectClientId,
UuidProvider.generateInequalityCheckingLambda(client.getId()),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle PATCH client redirect request - The redirect's [clientId:%s] does not"
+ " match provided [clientId:%s]",
redirectClientId, clientId));
final var assigned = assignNonNull(clientRedirectPatch, redirect, List.of("deleted"));
final var updatedRedirect = redirectService.update(assigned);
final var response =
ResponseFactory.createClientRedirect(updatedRedirect);
final var headers =
LinkHeaderUtil.createHeaders(new Class<?>[]{UUID.class, UUID.class, ClientRedirectPatch.class}, clientId, redirectId, clientRedirectPatch);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<List<ClientKeyReset>> getClientKeyResets(
UUID clientId, Integer offset, Integer limit) {
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle GET client key resets request - Could not find client with id: %s",
clientId));
final var pageable = OffsetBasedPageRequest.of(offset, limit);
final var resets = clientService.getAllClientKeyResetsByClientId(client, pageable);
final var response =
resets.stream()
.map(
ResponseFactory::createClientKeyReset)
.toList();
final var headers =
LinkHeaderUtil.createHeadersWithPagination(
resets, new Class<?>[]{UUID.class, Integer.class, Integer.class}, clientId, (int) pageable.getOffset(), pageable.getPageSize());
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<ClientKeyOtc> postClientKeyReset(UUID clientId, ClientKeyResetOtc clientKeyResetOtc) {
final var loggedInClientAndRole = loggedInClientAndRole();
final var loggedInClientId = loggedInClientAndRole.clientId();
final var loggedInRole = loggedInClientAndRole.role();
earlyExit(
loggedInClientId,
id -> (!loggedInRole.equals("admin") && !id.equals(clientId.toString())),
HttpStatus.UNAUTHORIZED,
String.format(
"Failed to handle POST client key reset request - Requested id: %s does not match logged in client id: %s",
clientId.toString(),
loggedInClientId));
final var client =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle POST client key reset request - Could not find client with id: %s",
clientId));
final var providedKeyResetString = (clientKeyResetOtc != null) ? clientKeyResetOtc.getKeyResetCode() : "";
earlyExit(
client.getKeyResetCode(),
clientKeyResetCode -> (!loggedInRole.equals("admin") && !clientKeyResetCode.equals(providedKeyResetString)),
HttpStatus.UNAUTHORIZED,
String.format(
"Failed to handle POST client key reset request - Provided key reset code: %s does not match the one generated for the client with id: %s",
providedKeyResetString,
clientId));
final var clientKeyResetEntity = clientService.keyReset(client);
final var response = ResponseFactory.createClientKeyOtc(clientKeyResetEntity);
final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{UUID.class, ClientKeyResetOtc.class}, clientId, clientKeyResetOtc);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(response);
}
@Override
public ResponseEntity<ClientKey> postClientKey(UUID clientId, ClientKeyOtc clientKeyOtc) {
final var clientEntity =
throwApiExceptionOnAbsentValue(
clientService.getClientById(clientId),
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle POST key request - Could not find client with id: %s",
clientId));
final var optionalKeyReset = clientService.getKeyReset(clientEntity, clientKeyOtc.getCode());
final var keyReset =
throwApiExceptionOnAbsentValue(
optionalKeyReset,
HttpStatus.NOT_FOUND,
String.format(
"Failed to handle POST key request - Could not find key reset for code: %s", clientKeyOtc.getCode()));
final var now = TimeProvider.now();
earlyExit(
keyReset.getCreated(),
created -> created.isBefore(now.minusDays(1)),
HttpStatus.GONE,
String.format(
"Failed to handle POST key request - code: %s is more then a day old", clientKeyOtc.getCode()));
earlyExit(
keyReset.getAccessed(),
Objects::nonNull,
HttpStatus.GONE,
String.format(
"Failed to handle POST key request - Key has already been accessed by using code: %s", clientKeyOtc.getCode()));
final var clientKey = clientService.key(clientEntity, keyReset);
final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{UUID.class, ClientKeyOtc.class}, clientId, clientKeyOtc);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.headers(headers)
.body(clientKey);
}
}