-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #81: Create endpoint for obtaining a new activation code
- Loading branch information
1 parent
3fbe326
commit 4ae294c
Showing
12 changed files
with
546 additions
and
8 deletions.
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
98
...rc/main/java/com/wultra/app/enrollmentserver/controller/api/ActivationCodeController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* PowerAuth Enrollment Server | ||
* Copyright (C) 2021 Wultra s.r.o. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package com.wultra.app.enrollmentserver.controller.api; | ||
|
||
import com.wultra.app.enrollmentserver.errorhandling.ActivationCodeException; | ||
import com.wultra.app.enrollmentserver.errorhandling.InvalidRequestObjectException; | ||
import com.wultra.app.enrollmentserver.impl.service.ActivationCodeService; | ||
import com.wultra.app.enrollmentserver.model.request.ActivationCodeRequest; | ||
import com.wultra.app.enrollmentserver.model.response.ActivationCodeResponse; | ||
import io.getlime.core.rest.model.base.request.ObjectRequest; | ||
import io.getlime.core.rest.model.base.response.ObjectResponse; | ||
import io.getlime.security.powerauth.crypto.lib.encryptor.ecies.model.EciesScope; | ||
import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes; | ||
import io.getlime.security.powerauth.rest.api.base.authentication.PowerAuthApiAuthentication; | ||
import io.getlime.security.powerauth.rest.api.base.exception.PowerAuthAuthenticationException; | ||
import io.getlime.security.powerauth.rest.api.spring.annotation.EncryptedRequestBody; | ||
import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuth; | ||
import io.getlime.security.powerauth.rest.api.spring.annotation.PowerAuthEncryption; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestMethod; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
/** | ||
* Controller publishing REST services for obtaining a new activation code. | ||
* | ||
* @author Petr Dvorak, petr@wultra.com | ||
*/ | ||
@ConditionalOnProperty( | ||
value = "enrollment-server.activation-spawn.enabled", | ||
havingValue = "true" | ||
) | ||
@RestController | ||
@RequestMapping(value = "api/activation") | ||
public class ActivationCodeController { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(ActivationCodeController.class); | ||
|
||
private final ActivationCodeService activationCodeService; | ||
|
||
/** | ||
* Default autowiring constructor. | ||
* | ||
* @param activationCodeService Activation code service. | ||
*/ | ||
@Autowired | ||
public ActivationCodeController(ActivationCodeService activationCodeService) { | ||
this.activationCodeService = activationCodeService; | ||
} | ||
|
||
/** | ||
* Controller request handler for requesting the activation code. | ||
* | ||
* @param request Request with activation OTP. | ||
* @param apiAuthentication Authentication object with user and app details. | ||
* @return New activation code, activation code signature and activation ID. | ||
* @throws PowerAuthAuthenticationException In case user authentication fails. | ||
* @throws InvalidRequestObjectException In case the object validation fails. | ||
* @throws ActivationCodeException In case fetching the activation code fails. | ||
*/ | ||
@RequestMapping(value = "code", method = RequestMethod.POST) | ||
@PowerAuthEncryption(scope = EciesScope.ACTIVATION_SCOPE) | ||
@PowerAuth(resourceId = "api/activation/code", signatureType = { | ||
PowerAuthSignatureTypes.POSSESSION_BIOMETRY, | ||
PowerAuthSignatureTypes.POSSESSION_KNOWLEDGE | ||
}) | ||
public ObjectResponse<ActivationCodeResponse> requestActivationCode(@EncryptedRequestBody ObjectRequest<ActivationCodeRequest> request, @Parameter(hidden = true) PowerAuthApiAuthentication apiAuthentication) throws PowerAuthAuthenticationException, InvalidRequestObjectException, ActivationCodeException { | ||
// Check if the authentication object is present | ||
if (apiAuthentication == null) { | ||
logger.error("Unable to verify device registration when fetching activation code"); | ||
throw new PowerAuthAuthenticationException("Unable to verify device registration when fetching activation code"); | ||
} | ||
|
||
// Request the activation code details. | ||
final ActivationCodeResponse response = activationCodeService.requestActivationCode(request.getRequestObject(), apiAuthentication); | ||
return new ObjectResponse<>(response); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
.../src/main/java/com/wultra/app/enrollmentserver/errorhandling/ActivationCodeException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* PowerAuth Enrollment Server | ||
* Copyright (C) 2021 Wultra s.r.o. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package com.wultra.app.enrollmentserver.errorhandling; | ||
|
||
/** | ||
* Exception thrown in case activation code cannot be fetched for any reason. | ||
* | ||
* @author Petr Dvorak, petr@wultra.com | ||
*/ | ||
public class ActivationCodeException extends Exception { | ||
private static final long serialVersionUID = 368452747764248241L; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
...er/src/main/java/com/wultra/app/enrollmentserver/impl/service/ActivationCodeDelegate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* PowerAuth Enrollment Server | ||
* Copyright (C) 2021 Wultra s.r.o. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package com.wultra.app.enrollmentserver.impl.service; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* Callback provider for lifecycle events of activation code. | ||
* | ||
* @author Petr Dvorak, petr@wultra.com | ||
*/ | ||
public interface ActivationCodeDelegate { | ||
|
||
/** | ||
* Fetch destination application ID value based in application ID. Check if the source app can | ||
* activate the destination one. | ||
* | ||
* @param applicationId Application identifier for app lookup. | ||
* @param sourceAppId Source application ID. | ||
* @param activationFlags Activation flags. | ||
* @param applicationRoles Application roles. | ||
* @return Destination application ID. | ||
*/ | ||
Long destinationApplicationId(String applicationId, Long sourceAppId, List<String> activationFlags, List<String> applicationRoles); | ||
|
||
/** | ||
* Callback method to add new activation flags to activation. | ||
* | ||
* @param sourceActivationId Source activation ID (activation used to fetch the code). | ||
* @param sourceActivationFlags Source activation flags (flags of the activation that initiated the transfer). | ||
* @param userId User ID (user ID who requested the activation). | ||
* @param sourceAppId Source app ID (the app that initiated the process). | ||
* @param sourceApplicationRoles Source application roles (roles of the app that intiated the transfer). | ||
* @param destinationAppId Destination app ID (the app that is to be activated). | ||
* @param destinationActivationId Destination activation ID (the activation ID of the new activation). | ||
* @param activationCode Activation code of the new activation. | ||
* @param activationCodeSignature Activation code signature of the new activation code. | ||
* @return List of new activation flags for the destination activation. | ||
*/ | ||
List<String> addActivationFlags(String sourceActivationId, List<String> sourceActivationFlags, String userId, Long sourceAppId, List<String> sourceApplicationRoles, Long destinationAppId, String destinationActivationId, String activationCode, String activationCodeSignature); | ||
|
||
/** | ||
* Callback method with newly created activation code information. | ||
* | ||
* @param sourceActivationId Source activation ID (activation used to fetch the code). | ||
* @param userId User ID (user ID who requested the activation). | ||
* @param sourceAppId Source app ID (the app that initiated the process). | ||
* @param destinationAppId Destination app ID (the app that is to be activated). | ||
* @param destinationActivationId Destination activation ID (the activation ID of the new activation). | ||
* @param activationCode Activation code of the new activation. | ||
* @param activationCodeSignature Activation code signature of the new activation code. | ||
*/ | ||
void didReturnActivationCode(String sourceActivationId, String userId, Long sourceAppId, Long destinationAppId, String destinationActivationId, String activationCode, String activationCodeSignature); | ||
|
||
} |
153 changes: 153 additions & 0 deletions
153
...ver/src/main/java/com/wultra/app/enrollmentserver/impl/service/ActivationCodeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
* PowerAuth Enrollment Server | ||
* Copyright (C) 2021 Wultra s.r.o. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package com.wultra.app.enrollmentserver.impl.service; | ||
|
||
import com.wultra.app.enrollmentserver.errorhandling.ActivationCodeException; | ||
import com.wultra.app.enrollmentserver.errorhandling.InvalidRequestObjectException; | ||
import com.wultra.app.enrollmentserver.impl.service.converter.ActivationCodeConverter; | ||
import com.wultra.app.enrollmentserver.model.request.ActivationCodeRequest; | ||
import com.wultra.app.enrollmentserver.model.response.ActivationCodeResponse; | ||
import com.wultra.app.enrollmentserver.model.validator.ActivationCodeRequestValidator; | ||
import com.wultra.security.powerauth.client.PowerAuthClient; | ||
import com.wultra.security.powerauth.client.model.error.PowerAuthClientException; | ||
import com.wultra.security.powerauth.client.v3.ActivationOtpValidation; | ||
import com.wultra.security.powerauth.client.v3.InitActivationResponse; | ||
import io.getlime.security.powerauth.rest.api.base.authentication.PowerAuthApiAuthentication; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* Service responsible for fetching the new activation codes. | ||
* | ||
* @author Petr Dvorak, petr@wultra.com | ||
*/ | ||
@Service | ||
public class ActivationCodeService { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(ActivationCodeService.class); | ||
|
||
private final PowerAuthClient powerAuthClient; | ||
private final ActivationCodeConverter activationCodeConverter; | ||
|
||
private ActivationCodeDelegate activationCodeDelegate; | ||
|
||
/** | ||
* Autowiring constructor. | ||
* | ||
* @param powerAuthClient PowerAuth Client instance. | ||
* @param activationCodeConverter Activation code converter class. | ||
*/ | ||
@Autowired | ||
public ActivationCodeService(PowerAuthClient powerAuthClient, ActivationCodeConverter activationCodeConverter) { | ||
this.powerAuthClient = powerAuthClient; | ||
this.activationCodeConverter = activationCodeConverter; | ||
} | ||
|
||
/** | ||
* Set activation code delegate via auto-wiring. | ||
* | ||
* @param activationCodeDelegate Activation code delegate bean. | ||
*/ | ||
@Autowired(required = false) | ||
public void setActivationCodeDelegate(ActivationCodeDelegate activationCodeDelegate) { | ||
this.activationCodeDelegate = activationCodeDelegate; | ||
} | ||
|
||
/** | ||
* Request activation code for provided OTP value, user ID and app ID. | ||
* | ||
* @param request Request with OTP value. | ||
* @param apiAuthentication Authentication object. | ||
* @return Response with activation code, activation code signature, and activation ID. | ||
* @throws ActivationCodeException In case of invalid user / app attributes or communication with PowerAuth Service fails. | ||
* @throws InvalidRequestObjectException In case request object validation fails. | ||
*/ | ||
public ActivationCodeResponse requestActivationCode(ActivationCodeRequest request, PowerAuthApiAuthentication apiAuthentication) throws InvalidRequestObjectException, ActivationCodeException { | ||
|
||
// Fetch information from the authentication object | ||
final String sourceActivationId = apiAuthentication.getActivationId(); | ||
final String sourceUserId = apiAuthentication.getUserId(); | ||
final Long sourceAppId = apiAuthentication.getApplicationId(); | ||
final List<String> sourceActivationFlags = apiAuthentication.getActivationFlags(); | ||
final List<String> sourceApplicationRoles = apiAuthentication.getApplicationRoles(); | ||
|
||
logger.info("Activation code registration started, user ID: {}", sourceUserId); | ||
|
||
// Verify that activation code delegate is implemented | ||
if (activationCodeDelegate == null) { | ||
logger.error("Missing activation code implementation"); | ||
throw new ActivationCodeException(); | ||
} | ||
|
||
// Validate the request object | ||
final String error = ActivationCodeRequestValidator.validate(request); | ||
if (error != null) { | ||
logger.error("Invalid object in activation code request - {}, user ID: {}", error, sourceUserId); | ||
throw new InvalidRequestObjectException(); | ||
} | ||
|
||
// Get the request parameters | ||
final String otp = request.getOtp(); | ||
final String applicationId = request.getApplicationId(); | ||
|
||
final Long destinationAppId = activationCodeDelegate.destinationApplicationId(applicationId, sourceAppId, sourceActivationFlags, sourceApplicationRoles); | ||
if (destinationAppId == null) { | ||
logger.error("Invalid application ID. The provided source app ID: {} cannot activate the destination app ID: {}.", sourceAppId, applicationId); | ||
throw new ActivationCodeException(); | ||
} | ||
|
||
try { | ||
// Create a new activation | ||
logger.info("Calling PowerAuth Server with new activation request, user ID: {}, app ID: {}, otp: {}, ", sourceUserId, destinationAppId, otp); | ||
final InitActivationResponse iar = powerAuthClient.initActivation( | ||
sourceUserId, destinationAppId, ActivationOtpValidation.ON_KEY_EXCHANGE, otp | ||
); | ||
logger.info("Successfully obtained a new activation with ID: {}", iar.getActivationId()); | ||
|
||
// Notify systems about newly created activation | ||
activationCodeDelegate.didReturnActivationCode( | ||
sourceActivationId, sourceUserId, sourceAppId, destinationAppId, | ||
iar.getActivationId(), iar.getActivationCode(), iar.getActivationSignature() | ||
); | ||
|
||
// Add the activation flags | ||
final List<String> flags = activationCodeDelegate.addActivationFlags( | ||
sourceActivationId, sourceActivationFlags, sourceUserId, sourceAppId, sourceApplicationRoles, destinationAppId, | ||
iar.getActivationId(), iar.getActivationCode(), iar.getActivationSignature() | ||
); | ||
if (flags != null && !flags.isEmpty()) { | ||
logger.info("Calling PowerAuth Server to add activation flags to activation ID: {}, flags: {}.", iar.getActivationId(), flags.toArray()); | ||
powerAuthClient.addActivationFlags(iar.getActivationId(), flags); | ||
logger.info("Successfully added flags to activation ID: {}.", iar.getActivationId()); | ||
} else { | ||
logger.info("Activation with ID: {} has no additional flags.", iar.getActivationId()); | ||
} | ||
|
||
return activationCodeConverter.convert(iar); | ||
} catch (PowerAuthClientException e) { | ||
logger.error("Unable to call upstream service.", e); | ||
throw new ActivationCodeException(); | ||
} | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.