io.gravitee.rest.api.service.impl.ApiKeyServiceImpl Maven / Gradle / Ivy
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.service.impl;
import static io.gravitee.repository.management.model.ApiKey.AuditEvent.*;
import static io.gravitee.repository.management.model.Audit.AuditProperties.*;
import io.gravitee.repository.exceptions.TechnicalException;
import io.gravitee.repository.management.api.ApiKeyRepository;
import io.gravitee.repository.management.model.ApiKey;
import io.gravitee.repository.management.model.Audit;
import io.gravitee.rest.api.model.*;
import io.gravitee.rest.api.service.*;
import io.gravitee.rest.api.service.exceptions.*;
import io.gravitee.rest.api.service.notification.ApiHook;
import io.gravitee.rest.api.service.notification.NotificationParamsBuilder;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author David BRASSELY (david.brassely at graviteesource.com)
* @author Nicolas GERAUD (nicolas.geraud at graviteesource.com)
* @author GraviteeSource Team
*/
@Component
public class ApiKeyServiceImpl extends TransactionalService implements ApiKeyService {
/**
* Logger.
*/
private final Logger LOGGER = LoggerFactory.getLogger(ApiKeyServiceImpl.class);
@Autowired
private ApiKeyRepository apiKeyRepository;
@Autowired
private SubscriptionService subscriptionService;
@Autowired
private ApiKeyGenerator apiKeyGenerator;
@Autowired
private ApplicationService applicationService;
@Autowired
private ApiService apiService;
@Autowired
private PlanService planService;
@Autowired
private AuditService auditService;
@Autowired
private NotifierService notifierService;
@Override
public ApiKeyEntity generate(String subscription) {
return generate(subscription, null);
}
@Override
public ApiKeyEntity generate(String subscription, String customApiKey) {
try {
LOGGER.debug("Generate an API Key for subscription {}", subscription);
ApiKey apiKey = generateForSubscription(subscription, customApiKey);
apiKey = apiKeyRepository.create(apiKey);
//TODO: Send a notification to the application owner
// Audit
final PlanEntity plan = planService.findById(apiKey.getPlan());
Map properties = new LinkedHashMap<>();
properties.put(API_KEY, apiKey.getKey());
properties.put(API, plan.getApi());
properties.put(APPLICATION, apiKey.getApplication());
auditService.createApiAuditLog(plan.getApi(), properties, APIKEY_CREATED, apiKey.getCreatedAt(), null, apiKey);
return convert(apiKey);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to generate an API Key for {} - {}", subscription, ex);
throw new TechnicalManagementException(
String.format("An error occurs while trying to generate an API Key for %s", subscription),
ex
);
}
}
@Override
public ApiKeyEntity renew(String subscription) {
return renew(subscription, null);
}
@Override
public ApiKeyEntity renew(String subscription, String customApiKey) {
try {
LOGGER.debug("Renew API Key for subscription {}", subscription);
ApiKey newApiKey = generateForSubscription(subscription, customApiKey);
newApiKey = apiKeyRepository.create(newApiKey);
Instant expirationInst = newApiKey.getCreatedAt().toInstant().plus(Duration.ofHours(2));
Date expirationDate = Date.from(expirationInst);
// Previously generated keys should be set as revoked
// Get previously generated keys to set their expiration date
Set oldKeys = apiKeyRepository.findBySubscription(subscription);
for (ApiKey oldKey : oldKeys) {
if (!oldKey.equals(newApiKey) && !convert(oldKey).isExpired()) {
setExpiration(expirationDate, oldKey);
}
}
// Audit
final PlanEntity plan = planService.findById(newApiKey.getPlan());
Map properties = new LinkedHashMap<>();
properties.put(API_KEY, newApiKey.getKey());
properties.put(API, plan.getApi());
properties.put(APPLICATION, newApiKey.getApplication());
auditService.createApiAuditLog(plan.getApi(), properties, APIKEY_RENEWED, newApiKey.getCreatedAt(), null, newApiKey);
// Notification
final ApplicationEntity application = applicationService.findById(newApiKey.getApplication());
final ApiModelEntity api = apiService.findByIdForTemplates(plan.getApi());
final PrimaryOwnerEntity owner = application.getPrimaryOwner();
final Map params = new NotificationParamsBuilder()
.application(application)
.plan(plan)
.api(api)
.owner(owner)
.apikey(newApiKey)
.build();
notifierService.trigger(ApiHook.APIKEY_RENEWED, plan.getApi(), params);
return convert(newApiKey);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to renew an API Key for {}", subscription, ex);
throw new TechnicalManagementException(
String.format("An error occurs while trying to renew an API Key for %s", subscription),
ex
);
}
}
/**
* Generate an {@link ApiKey} from a subscription
*
* @param subscription
* @return An Api Key
*/
private ApiKey generateForSubscription(String subscription) {
return generateForSubscription(subscription, null);
}
/**
* Generate an {@link ApiKey} from a subscription. If no custom API key, then generate a new one.
*
* @param subscription
* @param customApiKey
* @return An Api Key
*/
private ApiKey generateForSubscription(String subscription, String customApiKey) {
SubscriptionEntity subscriptionEntity = subscriptionService.findById(subscription);
Date now = new Date();
if (subscriptionEntity.getEndingAt() != null && subscriptionEntity.getEndingAt().before(now)) {
throw new SubscriptionClosedException(subscription);
}
ApiKey apiKey = new ApiKey();
apiKey.setSubscription(subscription);
apiKey.setApplication(subscriptionEntity.getApplication());
apiKey.setPlan(subscriptionEntity.getPlan());
apiKey.setCreatedAt(new Date());
apiKey.setUpdatedAt(apiKey.getCreatedAt());
apiKey.setKey(customApiKey != null ? customApiKey : apiKeyGenerator.generate());
// By default, the API Key will expire when subscription is closed
apiKey.setExpireAt(subscriptionEntity.getEndingAt());
return apiKey;
}
@Override
public void revoke(String apiKey, boolean notify) {
try {
LOGGER.debug("Revoke API Key {}", apiKey);
Optional optKey = apiKeyRepository.findById(apiKey);
if (!optKey.isPresent()) {
throw new ApiKeyNotFoundException();
}
ApiKey key = optKey.get();
checkApiKeyExpired(key);
ApiKey previousApiKey = new ApiKey(key);
key.setRevoked(true);
key.setUpdatedAt(new Date());
key.setRevokedAt(key.getUpdatedAt());
apiKeyRepository.update(key);
final PlanEntity plan = planService.findById(key.getPlan());
// Audit
Map properties = new LinkedHashMap<>();
properties.put(API_KEY, key.getKey());
properties.put(API, plan.getApi());
properties.put(APPLICATION, key.getApplication());
auditService.createApiAuditLog(plan.getApi(), properties, APIKEY_REVOKED, key.getUpdatedAt(), previousApiKey, key);
// notify
if (notify) {
final ApplicationEntity application = applicationService.findById(key.getApplication());
final ApiModelEntity api = apiService.findByIdForTemplates(plan.getApi());
final PrimaryOwnerEntity owner = application.getPrimaryOwner();
final Map params = new NotificationParamsBuilder()
.application(application)
.plan(plan)
.api(api)
.owner(owner)
.apikey(key)
.build();
notifierService.trigger(ApiHook.APIKEY_REVOKED, api.getId(), params);
}
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to revoke a key {}", apiKey, ex);
throw new TechnicalManagementException("An error occurs while trying to revoke a key " + apiKey, ex);
}
}
@Override
public ApiKeyEntity reactivate(String apiKey) {
try {
LOGGER.debug("Reactivate API Key {}", apiKey);
Optional optKey = apiKeyRepository.findById(apiKey);
if (!optKey.isPresent()) {
throw new ApiKeyNotFoundException();
}
ApiKey key = optKey.get();
if (!key.isRevoked() && !convert(key).isExpired()) {
throw new ApiKeyAlreadyActivatedException("The API key is already activated");
}
// Get the subscription to get ending date and set key expiration date.
SubscriptionEntity subscription = subscriptionService.findById(key.getSubscription());
if (subscription.getStatus() != SubscriptionStatus.PAUSED && subscription.getStatus() != SubscriptionStatus.ACCEPTED) {
throw new SubscriptionNotActiveException(subscription);
}
ApiKey previousApiKey = new ApiKey(key);
key.setRevoked(false);
key.setUpdatedAt(new Date());
key.setRevokedAt(null);
key.setExpireAt(subscription.getEndingAt());
ApiKey updated = apiKeyRepository.update(key);
// Audit
Map properties = new LinkedHashMap<>();
properties.put(API_KEY, key.getKey());
properties.put(API, subscription.getApi());
properties.put(APPLICATION, key.getApplication());
auditService.createApiAuditLog(subscription.getApi(), properties, APIKEY_REACTIVATED, key.getUpdatedAt(), previousApiKey, key);
return convert(updated);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to reactivate a key {}", apiKey, ex);
throw new TechnicalManagementException("An error occurs while trying to reactivate a key " + apiKey, ex);
}
}
private void checkApiKeyExpired(ApiKey key) {
if (key.isRevoked() || convert(key).isExpired()) {
throw new ApiKeyAlreadyExpiredException("The API key is already expired");
}
}
@Override
public List findBySubscription(String subscription) {
try {
LOGGER.debug("Find API Keys for subscription {}", subscription);
SubscriptionEntity subscriptionEntity = subscriptionService.findById(subscription);
Set keys = apiKeyRepository.findBySubscription(subscriptionEntity.getId());
return keys
.stream()
.map(ApiKeyServiceImpl::convert)
.sorted((o1, o2) -> o2.getCreatedAt().compareTo(o1.getCreatedAt()))
.collect(Collectors.toList());
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while finding API keys for subscription {}", subscription, ex);
throw new TechnicalManagementException(
String.format("An error occurs while finding API keys for subscription %s", subscription),
ex
);
}
}
@Override
public ApiKeyEntity findByKey(String apiKey) {
try {
LOGGER.debug("Find an API Key by key: {}", apiKey);
Optional optApiKey = apiKeyRepository.findById(apiKey);
if (optApiKey.isPresent()) {
return convert(optApiKey.get());
}
throw new ApiKeyNotFoundException();
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to find an API Key by key {}", apiKey, ex);
throw new TechnicalManagementException(String.format("An error occurs while trying to find an API Key by key: %s", apiKey), ex);
}
}
@Override
public ApiKeyEntity update(ApiKeyEntity apiKeyEntity) {
try {
LOGGER.debug("Update API Key {}", apiKeyEntity.getKey());
Optional optKey = apiKeyRepository.findById(apiKeyEntity.getKey());
if (!optKey.isPresent()) {
throw new ApiKeyNotFoundException();
}
ApiKey key = optKey.get();
checkApiKeyExpired(key);
key.setPaused(apiKeyEntity.isPaused());
key.setPlan(apiKeyEntity.getPlan());
if (apiKeyEntity.getExpireAt() != null) {
setExpiration(apiKeyEntity.getExpireAt(), key);
} else {
key.setUpdatedAt(new Date());
apiKeyRepository.update(key);
}
return convert(key);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while updating an API Key {}", apiKeyEntity.getKey(), ex);
throw new TechnicalManagementException(
String.format("An error occurs while updating an API Key %s", apiKeyEntity.getKey()),
ex
);
}
}
@Override
public boolean exists(String apiKey) {
LOGGER.debug("Check if an API Key exists by key: {}", apiKey);
try {
return apiKeyRepository.findById(apiKey).isPresent();
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while checking if API Key {} exists", apiKey, ex);
throw new TechnicalManagementException(String.format("An error occurs while checking if API Key %s exists", apiKey), ex);
}
}
@Override
public void delete(String apiKey) {
/*
try {
LOGGER.debug("Delete API Key {}", apiKey);
Optional optKey = apiKeyRepository.de(apiKey);
if (!optKey.isPresent()) {
throw new ApiKeyNotFoundException();
}
ApiKey key = optKey.get();
setExpiration(apiKeyEntity.getExpireAt(), key);
return convert(key);
} catch (TechnicalException ex) {
LOGGER.error("An error occurs while trying to update a key {}", apiKey, ex);
throw new TechnicalManagementException("An error occurs while trying to update a key " + apiKey, ex);
}
*/
}
private void setExpiration(Date expirationDate, ApiKey key) throws TechnicalException {
final Date now = new Date();
if (now.after(expirationDate)) {
expirationDate = now;
}
key.setUpdatedAt(now);
if (!key.isRevoked()) {
//the expired date must be <= than the subscription end date
SubscriptionEntity subscription = subscriptionService.findById(key.getSubscription());
if (
subscription.getEndingAt() != null && (expirationDate == null || subscription.getEndingAt().compareTo(expirationDate) < 0)
) {
expirationDate = subscription.getEndingAt();
}
ApiKey oldkey = new ApiKey(key);
key.setExpireAt(expirationDate);
apiKeyRepository.update(key);
//notify
final ApplicationEntity application = applicationService.findById(key.getApplication());
final PlanEntity plan = planService.findById(key.getPlan());
final ApiModelEntity api = apiService.findByIdForTemplates(plan.getApi());
final PrimaryOwnerEntity owner = application.getPrimaryOwner();
NotificationParamsBuilder paramsBuilder = new NotificationParamsBuilder();
paramsBuilder.api(api).application(application).apikey(key).plan(plan).owner(owner);
if (key.getExpireAt() != null && now.before(key.getExpireAt())) {
paramsBuilder.expirationDate(key.getExpireAt());
}
final Map params = paramsBuilder.build();
notifierService.trigger(ApiHook.APIKEY_EXPIRED, api.getId(), params);
// Audit
Map properties = new LinkedHashMap<>();
properties.put(API_KEY, key.getKey());
properties.put(API, api.getId());
properties.put(APPLICATION, application.getId());
auditService.createApiAuditLog(plan.getApi(), properties, APIKEY_EXPIRED, key.getUpdatedAt(), oldkey, key);
} else {
apiKeyRepository.update(key);
}
}
private static ApiKeyEntity convert(ApiKey apiKey) {
ApiKeyEntity apiKeyEntity = new ApiKeyEntity();
apiKeyEntity.setKey(apiKey.getKey());
apiKeyEntity.setCreatedAt(apiKey.getCreatedAt());
apiKeyEntity.setExpireAt(apiKey.getExpireAt());
apiKeyEntity.setExpired(apiKey.getExpireAt() != null && new Date().after(apiKey.getExpireAt()));
apiKeyEntity.setRevoked(apiKey.isRevoked());
apiKeyEntity.setRevokedAt(apiKey.getRevokedAt());
apiKeyEntity.setUpdatedAt(apiKey.getUpdatedAt());
apiKeyEntity.setSubscription(apiKey.getSubscription());
apiKeyEntity.setApplication(apiKey.getApplication());
apiKeyEntity.setPlan(apiKey.getPlan());
return apiKeyEntity;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy