package com.dbg.cloud.acheron.plugins.apikey.endpoints;
import com.dbg.cloud.acheron.adminendpoints.AdminEndpoint;
import com.dbg.cloud.acheron.consumers.Consumer;
import com.dbg.cloud.acheron.consumers.service.ConsumerService;
import com.dbg.cloud.acheron.plugins.apikey.store.APIKey;
import com.dbg.cloud.acheron.plugins.apikey.store.APIKeyStore;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/admin")
@AllArgsConstructor
@Slf4j
final class APIKeyController implements AdminEndpoint {
private final APIKeyStore apiKeyStore;
private final ConsumerService consumerService;
@RequestMapping(value = "/consumers/{consumerId}/api-keys", method = RequestMethod.GET)
public List<APIKeyTO> readAPIKeysOfConsumer(final @PathVariable String consumerId) {
final UUID uuidConsumerId = parseUUID(consumerId).orElseThrow(() -> new BadRequestException());
final List<APIKey> apiKeyList = apiKeyStore.findByConsumer(uuidConsumerId);
return apiKeyList.stream().map(apiKey -> new APIKeyTO(apiKey)).collect(Collectors.toList());
}
@RequestMapping(value = "/consumers/{consumerId}/api-keys/{apiKeyId}", method = RequestMethod.GET)
public ResponseEntity<?> readAPIKeyOfConsumer(final @PathVariable String consumerId,
final @PathVariable String apiKeyId) {
final UUID uuidConsumerId =
parseUUID(consumerId).orElseThrow(() -> new ConsumerNotFoundException(consumerId));
final UUID uuidAPIKeyId =
parseUUID(apiKeyId).orElseThrow(() -> new APIKeyNotFoundException(apiKeyId));
final Optional<APIKey> optionalAPIKey = apiKeyStore.findById(uuidAPIKeyId);
final APIKey apiKey = optionalAPIKey.orElseThrow(() -> new APIKeyNotFoundException(apiKeyId));
if (!uuidConsumerId.equals(apiKey.getConsumerId())) {
throw new ConsumerNotFoundException(consumerId);
}
return ResponseEntity.ok(new APIKeyTO(apiKey));
}
@RequestMapping(value = "/consumers/{consumerId}/api-keys", method = RequestMethod.POST)
public ResponseEntity<?> addAPIKeyToConsumer(final @PathVariable String consumerId,
final @JsonView(View.Register.class) @RequestBody APIKeyTO apiKey) {
if (!validateRegistration(apiKey)) {
return ResponseEntity.badRequest().build();
}
final UUID uuidConsumerId = parseUUID(consumerId).orElseThrow(() -> new ConsumerNotFoundException(consumerId));
final Consumer consumer = consumerService.getConsumer(uuidConsumerId).orElseThrow(
() -> new ConsumerNotFoundException(consumerId));
return ResponseEntity
.status(HttpStatus.CREATED)
.body(new APIKeyTO(
apiKeyStore.add(new APIKey.ForCreation(apiKey.getApiKey(), consumer.getId(), consumer.getName(),
consumer.getCreatedAt()))));
}
@RequestMapping(value = "/consumers/{consumerId}/api-keys/{apiKeyId}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteAPIKeyOfConsumer(final @PathVariable String consumerId,
final @PathVariable String apiKeyId) {
final UUID uuidConsumerId = parseUUID(consumerId).orElseThrow(() -> new ConsumerNotFoundException(consumerId));
final UUID uuidAPIKeyId = parseUUID(apiKeyId).orElseThrow(() -> new APIKeyNotFoundException(apiKeyId));
final APIKey apiKey = apiKeyStore.findById(uuidAPIKeyId).orElseThrow(
() -> new APIKeyNotFoundException(apiKeyId));
if (uuidConsumerId.equals(apiKey.getConsumerId())) {
apiKeyStore.deleteById(uuidAPIKeyId);
} else {
throw new APIKeyNotFoundException(apiKeyId);
}
return ResponseEntity.noContent().build();
}
@RequestMapping(value = "/plugins/api-key/keys/{apiKeyId}", method = RequestMethod.GET)
public ResponseEntity<?> readAPIKey(final @PathVariable String apiKeyId) {
final UUID uuidAPIKeyId =
parseUUID(apiKeyId).orElseThrow(() -> new APIKeyNotFoundException(apiKeyId));
final Optional<APIKey> optionalAPIKey = apiKeyStore.findById(uuidAPIKeyId);
return ResponseEntity.ok(new APIKeyTO(
optionalAPIKey.orElseThrow(() -> new APIKeyNotFoundException(apiKeyId))));
}
@RequestMapping(value = "/plugins/api-key/keys/{apiKeyId}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteAPIKey(final @PathVariable String apiKeyId) {
final UUID uuidAPIKeyId =
parseUUID(apiKeyId).orElseThrow(() -> new APIKeyNotFoundException(apiKeyId));
apiKeyStore.deleteById(uuidAPIKeyId);
return ResponseEntity.noContent().build();
}
private boolean validateRegistration(final APIKeyTO apiKey) {
// if present, API key must not be an empty string
return apiKey.getApiKey() == null || (apiKey.getApiKey() != null && !apiKey.getApiKey().trim().isEmpty());
}
private Optional<UUID> parseUUID(final String candidateUUID) {
UUID uuid = null;
try {
uuid = UUID.fromString(candidateUUID);
} catch (Exception e) {
log.info("Passed id is not a UUID {}", candidateUUID);
}
return Optional.ofNullable(uuid);
}
@ResponseStatus(HttpStatus.NOT_FOUND)
class ConsumerNotFoundException extends RuntimeException {
public ConsumerNotFoundException(final String consumerId) {
super("Could not find consumer '" + consumerId + "'");
}
}
@ResponseStatus(HttpStatus.NOT_FOUND)
class APIKeyNotFoundException extends RuntimeException {
public APIKeyNotFoundException(final String apiKeyId) {
super("Could not find api key '" + apiKeyId + "'");
}
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
class BadRequestException extends RuntimeException {
}
}