Search in sources :

Example 1 with FactorNotFoundException

use of io.gravitee.am.service.exception.FactorNotFoundException in project gravitee-access-management by gravitee-io.

the class MFAChallengeEndpoint method getEnrolledFactor.

private EnrolledFactor getEnrolledFactor(RoutingContext routingContext, Factor factor, User endUser) {
    // enrolled factor can be either in session (if user come from mfa/enroll page)
    // or from the user enrolled factor list
    final String savedFactorId = routingContext.session().get(ConstantKeys.ENROLLED_FACTOR_ID_KEY);
    if (factor.getId().equals(savedFactorId)) {
        EnrolledFactor enrolledFactor = new EnrolledFactor();
        enrolledFactor.setFactorId(factor.getId());
        switch(factor.getFactorType()) {
            case OTP:
                enrolledFactor.setSecurity(new EnrolledFactorSecurity(SHARED_SECRET, routingContext.session().get(ConstantKeys.ENROLLED_FACTOR_SECURITY_VALUE_KEY)));
                break;
            case SMS:
                enrolledFactor.setChannel(new EnrolledFactorChannel(Type.SMS, routingContext.session().get(ConstantKeys.ENROLLED_FACTOR_PHONE_NUMBER)));
                break;
            case CALL:
                enrolledFactor.setChannel(new EnrolledFactorChannel(Type.CALL, routingContext.session().get(ConstantKeys.ENROLLED_FACTOR_PHONE_NUMBER)));
                break;
            case EMAIL:
                Map<String, Object> additionalData = new Maps.MapBuilder(new HashMap()).put(FactorDataKeys.KEY_MOVING_FACTOR, generateInitialMovingFactor(endUser)).build();
                // For email even if the endUser will contains all relevant information, we extract only the Expiration Date of the code.
                // this is done only to enforce the other parameter (shared secret and initialMovingFactor)
                getEnrolledFactor(factor, endUser).ifPresent(ef -> {
                    additionalData.put(FactorDataKeys.KEY_EXPIRE_AT, ef.getSecurity().getData(FactorDataKeys.KEY_EXPIRE_AT, Long.class));
                });
                enrolledFactor.setSecurity(new EnrolledFactorSecurity(SHARED_SECRET, routingContext.session().get(ConstantKeys.ENROLLED_FACTOR_SECURITY_VALUE_KEY), additionalData));
                enrolledFactor.setChannel(new EnrolledFactorChannel(Type.EMAIL, routingContext.session().get(ConstantKeys.ENROLLED_FACTOR_EMAIL_ADDRESS)));
                break;
            case RECOVERY_CODE:
                if (endUser.getFactors() != null) {
                    Optional<EnrolledFactorSecurity> factorSecurity = endUser.getFactors().stream().filter(ftr -> ftr.getSecurity().getType().equals(RECOVERY_CODE)).map(EnrolledFactor::getSecurity).findFirst();
                    factorSecurity.ifPresent(enrolledFactor::setSecurity);
                }
                break;
        }
        enrolledFactor.setCreatedAt(new Date());
        enrolledFactor.setUpdatedAt(enrolledFactor.getCreatedAt());
        return enrolledFactor;
    }
    return getEnrolledFactor(factor, endUser).orElseThrow(() -> new FactorNotFoundException("No enrolled factor found for the end user"));
}
Also used : EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException) EnrolledFactorChannel(io.gravitee.am.model.factor.EnrolledFactorChannel) EnrolledFactorSecurity(io.gravitee.am.model.factor.EnrolledFactorSecurity)

Example 2 with FactorNotFoundException

use of io.gravitee.am.service.exception.FactorNotFoundException in project gravitee-access-management by gravitee-io.

the class AccountFactorsEndpointHandler method getEnrolledFactorQrCode.

/**
 * Get QR code for the selected enrolled factor (TOTP only)
 *
 * @param routingContext the routingContext holding the current user
 */
public void getEnrolledFactorQrCode(RoutingContext routingContext) {
    final User user = routingContext.get(ConstantKeys.USER_CONTEXT_KEY);
    final String factorId = routingContext.request().getParam("factorId");
    final FactorProvider factorProvider = factorManager.get(factorId);
    if (factorProvider == null) {
        routingContext.fail(new FactorNotFoundException(factorId));
        return;
    }
    if (user.getFactors() == null) {
        routingContext.fail(new FactorNotFoundException(factorId));
        return;
    }
    Optional<EnrolledFactor> optionalEnrolledFactor = getEnrolledFactor(factorId, user);
    if (optionalEnrolledFactor.isEmpty()) {
        routingContext.fail(new FactorNotFoundException(factorId));
        return;
    }
    EnrolledFactor enrolledFactor = optionalEnrolledFactor.get();
    factorProvider.generateQrCode(user, enrolledFactor).subscribe(barCode -> AccountResponseHandler.handleDefaultResponse(routingContext, new JsonObject().put("qrCode", barCode)), error -> routingContext.fail(error), () -> routingContext.fail(404));
}
Also used : DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) User(io.gravitee.am.model.User) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException) JsonObject(io.vertx.core.json.JsonObject) FactorProvider(io.gravitee.am.factor.api.FactorProvider)

Example 3 with FactorNotFoundException

use of io.gravitee.am.service.exception.FactorNotFoundException in project gravitee-access-management by gravitee-io.

the class AccountFactorsEndpointHandler method verifyFactor.

/**
 * Verify a factor for the current user
 *
 * @param routingContext the routingContext holding the current user
 */
public void verifyFactor(RoutingContext routingContext) {
    try {
        if (routingContext.getBodyAsString() == null) {
            routingContext.fail(new InvalidRequestException("Unable to parse body message"));
            return;
        }
        final User user = routingContext.get(ConstantKeys.USER_CONTEXT_KEY);
        final String factorId = routingContext.request().getParam("factorId");
        final String code = routingContext.getBodyAsJson().getString("code");
        // code is required
        if (isEmpty(code)) {
            routingContext.fail(new InvalidRequestException("Field [code] is required"));
            return;
        }
        // find factor
        findFactor(factorId, h -> {
            if (h.failed()) {
                routingContext.fail(h.cause());
                return;
            }
            final FactorProvider factorProvider = factorManager.get(factorId);
            if (factorProvider == null) {
                routingContext.fail(new FactorNotFoundException(factorId));
                return;
            }
            // get enrolled factor for the current user
            Optional<EnrolledFactor> optionalEnrolledFactor = user.getFactors().stream().filter(enrolledFactor -> factorId.equals(enrolledFactor.getFactorId())).findFirst();
            if (!optionalEnrolledFactor.isPresent()) {
                routingContext.fail(new FactorNotFoundException(factorId));
                return;
            }
            // if factor is already activated, continue
            final EnrolledFactor enrolledFactor = optionalEnrolledFactor.get();
            if (FactorStatus.ACTIVATED.equals(enrolledFactor.getStatus())) {
                AccountResponseHandler.handleDefaultResponse(routingContext, enrolledFactor);
                return;
            }
            // verify factor
            verifyFactor(code, enrolledFactor, factorProvider, vh -> {
                if (vh.failed()) {
                    routingContext.fail(vh.cause());
                    return;
                }
                // verify successful, change the EnrolledFactor Status
                enrolledFactor.setStatus(FactorStatus.ACTIVATED);
                accountService.upsertFactor(user.getId(), enrolledFactor, new DefaultUser(user)).subscribe(__ -> AccountResponseHandler.handleDefaultResponse(routingContext, enrolledFactor), error -> routingContext.fail(error));
            });
        });
    } catch (DecodeException ex) {
        routingContext.fail(new InvalidRequestException("Unable to parse body message"));
    }
}
Also used : SHARED_SECRET(io.gravitee.am.common.factor.FactorSecurityType.SHARED_SECRET) FactorType(io.gravitee.am.common.factor.FactorType) Factor(io.gravitee.am.model.Factor) Json(io.vertx.core.json.Json) java.util(java.util) Client(io.gravitee.am.model.oidc.Client) DecodeException(io.vertx.core.json.DecodeException) Completable(io.reactivex.Completable) ConstantKeys(io.gravitee.am.common.utils.ConstantKeys) DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) EnrolledFactorChannel(io.gravitee.am.model.factor.EnrolledFactorChannel) SecureRandom(java.security.SecureRandom) KEY_USER(io.gravitee.am.factor.api.FactorContext.KEY_USER) RECOVERY_CODE(io.gravitee.am.common.factor.FactorSecurityType.RECOVERY_CODE) InvalidRequestException(io.gravitee.am.common.exception.oauth2.InvalidRequestException) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) Type(io.gravitee.am.model.factor.EnrolledFactorChannel.Type) FactorStatus(io.gravitee.am.model.factor.FactorStatus) Observable(io.reactivex.Observable) Schedulers(io.reactivex.schedulers.Schedulers) JsonObject(io.vertx.core.json.JsonObject) Objects.isNull(java.util.Objects.isNull) AsyncResult(io.vertx.core.AsyncResult) User(io.gravitee.am.model.User) InvalidFactorAttributeException(io.gravitee.am.common.exception.mfa.InvalidFactorAttributeException) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException) RoutingContextHelper.getEvaluableAttributes(io.gravitee.am.gateway.handler.common.utils.RoutingContextHelper.getEvaluableAttributes) FactorDataKeys(io.gravitee.am.common.factor.FactorDataKeys) VertxHttpServerRequest(io.gravitee.am.gateway.handler.common.vertx.core.http.VertxHttpServerRequest) Future(io.vertx.core.Future) RoutingContext(io.vertx.reactivex.ext.web.RoutingContext) ApplicationContext(org.springframework.context.ApplicationContext) Collectors(java.util.stream.Collectors) AccountService(io.gravitee.am.gateway.handler.account.services.AccountService) Maps(io.gravitee.common.util.Maps) StandardCharsets(java.nio.charset.StandardCharsets) EnrollmentAccount(io.gravitee.am.gateway.handler.account.model.EnrollmentAccount) EnrolledFactorSecurity(io.gravitee.am.model.factor.EnrolledFactorSecurity) EvaluableRequest(io.gravitee.gateway.api.el.EvaluableRequest) RecoveryFactor(io.gravitee.am.factor.api.RecoveryFactor) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) StringUtils.isEmpty(org.springframework.util.StringUtils.isEmpty) FactorContext(io.gravitee.am.factor.api.FactorContext) Enrollment(io.gravitee.am.factor.api.Enrollment) Handler(io.vertx.core.Handler) FactorProvider(io.gravitee.am.factor.api.FactorProvider) FactorManager(io.gravitee.am.gateway.handler.common.factor.FactorManager) DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) User(io.gravitee.am.model.User) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException) InvalidRequestException(io.gravitee.am.common.exception.oauth2.InvalidRequestException) DecodeException(io.vertx.core.json.DecodeException) FactorProvider(io.gravitee.am.factor.api.FactorProvider)

Example 4 with FactorNotFoundException

use of io.gravitee.am.service.exception.FactorNotFoundException in project gravitee-access-management by gravitee-io.

the class AccountFactorsEndpointHandler method getEnrolledFactor.

/**
 * Get enrolled factor detail for the current user
 *
 * @param routingContext the routingContext holding the current user
 */
public void getEnrolledFactor(RoutingContext routingContext) {
    final User user = routingContext.get(ConstantKeys.USER_CONTEXT_KEY);
    final String factorId = routingContext.request().getParam("factorId");
    if (user.getFactors() == null) {
        routingContext.fail(new FactorNotFoundException(factorId));
        return;
    }
    final Optional<EnrolledFactor> optionalEnrolledFactor = getEnrolledFactor(factorId, user);
    if (optionalEnrolledFactor.isEmpty()) {
        routingContext.fail(new FactorNotFoundException(factorId));
        return;
    }
    // Remove recovery code from enrolled factor
    EnrolledFactor enrolledFactor = optionalEnrolledFactor.get();
    if (RECOVERY_CODE.equals(enrolledFactor.getSecurity())) {
        routingContext.fail(new FactorNotFoundException(factorId));
        return;
    }
    AccountResponseHandler.handleDefaultResponse(routingContext, enrolledFactor);
}
Also used : DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) User(io.gravitee.am.model.User) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException)

Example 5 with FactorNotFoundException

use of io.gravitee.am.service.exception.FactorNotFoundException in project gravitee-access-management by gravitee-io.

the class AccountFactorsEndpointHandler method enrollFactor.

/**
 * Enroll a factor for the current user
 *
 * @param routingContext the routingContext holding the current user
 */
public void enrollFactor(RoutingContext routingContext) {
    try {
        if (routingContext.getBodyAsString() == null) {
            routingContext.fail(new InvalidRequestException("Unable to parse body message"));
            return;
        }
        final User user = routingContext.get(ConstantKeys.USER_CONTEXT_KEY);
        final io.gravitee.am.gateway.handler.account.model.Enrollment enrollment = Json.decodeValue(routingContext.getBodyAsString(), io.gravitee.am.gateway.handler.account.model.Enrollment.class);
        // factorId is required
        if (isEmpty(enrollment.getFactorId())) {
            routingContext.fail(new InvalidRequestException("Field [factorId] is required"));
            return;
        }
        final String factorId = enrollment.getFactorId();
        final EnrollmentAccount account = enrollment.getAccount();
        // find factor
        findFactor(factorId, h -> {
            if (h.failed()) {
                routingContext.fail(h.cause());
                return;
            }
            final Factor factor = h.result();
            final FactorProvider factorProvider = factorManager.get(factorId);
            if (factorProvider == null) {
                routingContext.fail(new FactorNotFoundException(factorId));
                return;
            }
            if (isRecoveryCodeFactor(factor)) {
                routingContext.fail(new InvalidRequestException("Recovery code does not support enrollment feature. Instead, use '/api/recovery_code' endpoint to generate recovery code."));
                return;
            }
            // check request body parameters
            switch(factor.getFactorType()) {
                case CALL:
                case SMS:
                    if (isNull(account) || isEmpty(account.getPhoneNumber())) {
                        routingContext.fail(new InvalidRequestException("Field [phoneNumber] is required"));
                        return;
                    }
                    break;
                case EMAIL:
                    if (isNull(account) || isEmpty(account.getEmail())) {
                        routingContext.fail(new InvalidRequestException("Field [email] is required"));
                        return;
                    }
                    break;
                default:
                    // Do nothing
                    break;
            }
            // check if the current factor is already enrolled
            if (user.getFactors() != null) {
                Optional<EnrolledFactor> optionalEnrolledFactor = user.getFactors().stream().filter(enrolledFactor -> factorId.equals(enrolledFactor.getFactorId())).findFirst();
                if (optionalEnrolledFactor.isPresent()) {
                    EnrolledFactor existingEnrolledFactor = optionalEnrolledFactor.get();
                    if (FactorStatus.ACTIVATED.equals(existingEnrolledFactor.getStatus())) {
                        AccountResponseHandler.handleDefaultResponse(routingContext, existingEnrolledFactor);
                        return;
                    }
                }
            }
            // enroll factor
            enrollFactor(factor, factorProvider, account, user, eh -> {
                if (eh.failed()) {
                    if (eh.cause() instanceof InvalidFactorAttributeException) {
                        routingContext.fail(400, eh.cause());
                    } else {
                        routingContext.fail(eh.cause());
                    }
                    return;
                }
                final EnrolledFactor enrolledFactor = eh.result();
                // send challenge
                sendChallenge(factorProvider, enrolledFactor, user, routingContext, sh -> {
                    // save enrolled factor
                    accountService.upsertFactor(user.getId(), enrolledFactor, new DefaultUser(user)).subscribe(__ -> AccountResponseHandler.handleDefaultResponse(routingContext, enrolledFactor), error -> routingContext.fail(error));
                });
            });
        });
    } catch (DecodeException ex) {
        routingContext.fail(new InvalidRequestException("Unable to parse body message"));
    }
}
Also used : SHARED_SECRET(io.gravitee.am.common.factor.FactorSecurityType.SHARED_SECRET) FactorType(io.gravitee.am.common.factor.FactorType) Factor(io.gravitee.am.model.Factor) Json(io.vertx.core.json.Json) java.util(java.util) Client(io.gravitee.am.model.oidc.Client) DecodeException(io.vertx.core.json.DecodeException) Completable(io.reactivex.Completable) ConstantKeys(io.gravitee.am.common.utils.ConstantKeys) DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) EnrolledFactorChannel(io.gravitee.am.model.factor.EnrolledFactorChannel) SecureRandom(java.security.SecureRandom) KEY_USER(io.gravitee.am.factor.api.FactorContext.KEY_USER) RECOVERY_CODE(io.gravitee.am.common.factor.FactorSecurityType.RECOVERY_CODE) InvalidRequestException(io.gravitee.am.common.exception.oauth2.InvalidRequestException) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) Type(io.gravitee.am.model.factor.EnrolledFactorChannel.Type) FactorStatus(io.gravitee.am.model.factor.FactorStatus) Observable(io.reactivex.Observable) Schedulers(io.reactivex.schedulers.Schedulers) JsonObject(io.vertx.core.json.JsonObject) Objects.isNull(java.util.Objects.isNull) AsyncResult(io.vertx.core.AsyncResult) User(io.gravitee.am.model.User) InvalidFactorAttributeException(io.gravitee.am.common.exception.mfa.InvalidFactorAttributeException) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException) RoutingContextHelper.getEvaluableAttributes(io.gravitee.am.gateway.handler.common.utils.RoutingContextHelper.getEvaluableAttributes) FactorDataKeys(io.gravitee.am.common.factor.FactorDataKeys) VertxHttpServerRequest(io.gravitee.am.gateway.handler.common.vertx.core.http.VertxHttpServerRequest) Future(io.vertx.core.Future) RoutingContext(io.vertx.reactivex.ext.web.RoutingContext) ApplicationContext(org.springframework.context.ApplicationContext) Collectors(java.util.stream.Collectors) AccountService(io.gravitee.am.gateway.handler.account.services.AccountService) Maps(io.gravitee.common.util.Maps) StandardCharsets(java.nio.charset.StandardCharsets) EnrollmentAccount(io.gravitee.am.gateway.handler.account.model.EnrollmentAccount) EnrolledFactorSecurity(io.gravitee.am.model.factor.EnrolledFactorSecurity) EvaluableRequest(io.gravitee.gateway.api.el.EvaluableRequest) RecoveryFactor(io.gravitee.am.factor.api.RecoveryFactor) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) StringUtils.isEmpty(org.springframework.util.StringUtils.isEmpty) FactorContext(io.gravitee.am.factor.api.FactorContext) Enrollment(io.gravitee.am.factor.api.Enrollment) Handler(io.vertx.core.Handler) FactorProvider(io.gravitee.am.factor.api.FactorProvider) FactorManager(io.gravitee.am.gateway.handler.common.factor.FactorManager) DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) User(io.gravitee.am.model.User) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) FactorNotFoundException(io.gravitee.am.service.exception.FactorNotFoundException) EnrollmentAccount(io.gravitee.am.gateway.handler.account.model.EnrollmentAccount) DecodeException(io.vertx.core.json.DecodeException) FactorProvider(io.gravitee.am.factor.api.FactorProvider) InvalidFactorAttributeException(io.gravitee.am.common.exception.mfa.InvalidFactorAttributeException) DefaultUser(io.gravitee.am.identityprovider.api.DefaultUser) Factor(io.gravitee.am.model.Factor) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) UpdateEnrolledFactor(io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor) RecoveryFactor(io.gravitee.am.factor.api.RecoveryFactor) InvalidRequestException(io.gravitee.am.common.exception.oauth2.InvalidRequestException)

Aggregations

EnrolledFactor (io.gravitee.am.model.factor.EnrolledFactor)6 FactorNotFoundException (io.gravitee.am.service.exception.FactorNotFoundException)6 UpdateEnrolledFactor (io.gravitee.am.gateway.handler.account.model.UpdateEnrolledFactor)5 DefaultUser (io.gravitee.am.identityprovider.api.DefaultUser)5 User (io.gravitee.am.model.User)5 FactorProvider (io.gravitee.am.factor.api.FactorProvider)4 EnrolledFactorChannel (io.gravitee.am.model.factor.EnrolledFactorChannel)4 EnrolledFactorSecurity (io.gravitee.am.model.factor.EnrolledFactorSecurity)4 JsonObject (io.vertx.core.json.JsonObject)4 InvalidFactorAttributeException (io.gravitee.am.common.exception.mfa.InvalidFactorAttributeException)3 InvalidRequestException (io.gravitee.am.common.exception.oauth2.InvalidRequestException)3 FactorDataKeys (io.gravitee.am.common.factor.FactorDataKeys)3 RECOVERY_CODE (io.gravitee.am.common.factor.FactorSecurityType.RECOVERY_CODE)3 SHARED_SECRET (io.gravitee.am.common.factor.FactorSecurityType.SHARED_SECRET)3 FactorType (io.gravitee.am.common.factor.FactorType)3 ConstantKeys (io.gravitee.am.common.utils.ConstantKeys)3 Enrollment (io.gravitee.am.factor.api.Enrollment)3 FactorContext (io.gravitee.am.factor.api.FactorContext)3 KEY_USER (io.gravitee.am.factor.api.FactorContext.KEY_USER)3 RecoveryFactor (io.gravitee.am.factor.api.RecoveryFactor)3