ClientService.java

package se.jobtechdev.personaldatagateway.api.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import se.jobtechdev.personaldatagateway.api.generated.entities.ClientEntity;
import se.jobtechdev.personaldatagateway.api.generated.entities.ClientKeyResetEntity;
import se.jobtechdev.personaldatagateway.api.generated.entities.OrganizationEntity;
import se.jobtechdev.personaldatagateway.api.generated.model.ClientKey;
import se.jobtechdev.personaldatagateway.api.repository.ClientKeyResetRepository;
import se.jobtechdev.personaldatagateway.api.repository.ClientRepository;
import se.jobtechdev.personaldatagateway.api.util.KeyProvider;
import se.jobtechdev.personaldatagateway.api.util.TimeProvider;
import se.jobtechdev.personaldatagateway.api.util.UuidProvider;

import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.UUID;

import static se.jobtechdev.personaldatagateway.api.util.MessageDigestProvider.sha256;

@Component
public class ClientService {
  private final ClientRepository clientRepository;

  private final ClientKeyResetRepository clientKeyResetRepository;

  @SuppressWarnings("unused")
  @Autowired
  public ClientService(
      ClientRepository clientRepository, ClientKeyResetRepository clientKeyResetRepository) {
    this.clientRepository = clientRepository;
    this.clientKeyResetRepository = clientKeyResetRepository;
  }

  public Page<ClientEntity> getClients(Pageable pageable) {
    return clientRepository.findAll(pageable);
  }

  public Optional<ClientEntity> getClientById(UUID clientId) {
    return clientRepository.findById(clientId);
  }

  public Optional<ClientEntity> getClientByKeyHash(byte[] apiKeyHash) {
    return clientRepository.findByKeyHash(apiKeyHash);
  }

  @Transactional
  public ClientEntity createClient(
      OrganizationEntity organization, String name, String role, byte[] clientKey) {
    final var clientId = UuidProvider.uuid();
    final var now = TimeProvider.now();
    final var client = new ClientEntity(clientId, organization, sha256(clientKey), name, role, now, now);
    return clientRepository.save(client);
  }

  @Transactional
  public ClientEntity saveClient(ClientEntity client) {
    return clientRepository.save(client);
  }

  @Transactional
  public void delete(ClientEntity client) {
    clientRepository.delete(client);
  }

  public Page<ClientKeyResetEntity> getAllClientKeyResetsByClientId(ClientEntity client, Pageable pageable) {
    return clientKeyResetRepository.findAllByClientEntity(client, pageable);
  }

  @Transactional
  public ClientKeyResetEntity keyReset(ClientEntity client) {
    final var now = ZonedDateTime.now();

    final var code = KeyProvider.randomCode(6);

    // reset the hash
    final var hash = new byte[256];
    client.setKeyHash(hash);
    client.setUpdated(now);
    clientRepository.save(client);

    final var clientKeyReset = new ClientKeyResetEntity(UUID.randomUUID(), client, now, now, code, null);

    return clientKeyResetRepository.save(clientKeyReset);
  }

  public Optional<ClientKeyResetEntity> getKeyReset(ClientEntity client, String code) {
    return clientKeyResetRepository.findByClientEntityAndCode(client, code);
  }

  @Transactional
  public ClientKey key(ClientEntity client, ClientKeyResetEntity clientKeyReset) {
    final var now = ZonedDateTime.now();
    final var key = KeyProvider.randomBytes(32);
    final var keyHash = sha256(key);
    final var keyResetCode = KeyProvider.randomCode(6);
    client.setKeyHash(keyHash);
    client.setKeyResetCode(keyResetCode);
    client.setUpdated(now);
    clientRepository.save(client);

    final var base64EncodedKey = KeyProvider.b64EncodedKey(key);

    clientKeyReset.setAccessed(now);
    clientKeyReset.setUpdated(now);
    clientKeyResetRepository.save(clientKeyReset);

    return new ClientKey(client.getId(), base64EncodedKey, keyResetCode);
  }
}