Search in sources :

Example 1 with Enrollment

use of io.gravitee.am.factor.api.Enrollment 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)

Example 2 with Enrollment

use of io.gravitee.am.factor.api.Enrollment in project gravitee-access-management by gravitee-io.

the class MFAEnrollEndpoint method saveEnrollment.

private void saveEnrollment(RoutingContext routingContext) {
    MultiMap params = routingContext.request().formAttributes();
    final Boolean acceptEnrollment = Boolean.valueOf(params.get(ConstantKeys.USER_MFA_ENROLLMENT));
    final String factorId = params.get(ConstantKeys.MFA_ENROLLMENT_FACTOR_ID);
    final String sharedSecret = params.get(ConstantKeys.MFA_ENROLLMENT_SHARED_SECRET);
    final String phoneNumber = params.get(ConstantKeys.MFA_ENROLLMENT_PHONE);
    final String emailAddress = params.get(ConstantKeys.MFA_ENROLLMENT_EMAIL);
    if (factorId == null) {
        logger.warn("No factor id in form - did you forget to include factor id value ?");
        routingContext.fail(400);
        return;
    }
    final Client client = routingContext.get(ConstantKeys.CLIENT_CONTEXT_KEY);
    final Map<io.gravitee.am.model.Factor, FactorProvider> factors = getFactors(client);
    Optional<Map.Entry<io.gravitee.am.model.Factor, FactorProvider>> optFactor = factors.entrySet().stream().filter(factor -> factorId.equals(factor.getKey().getId())).findFirst();
    if (optFactor.isEmpty()) {
        logger.warn("Factor not found - did you send a valid factor id ?");
        routingContext.fail(400);
        return;
    }
    // if user has skipped the enrollment process, continue
    if (!acceptEnrollment) {
        final User endUser = ((io.gravitee.am.gateway.handler.common.vertx.web.auth.user.User) routingContext.user().getDelegate()).getUser();
        userService.setMfaEnrollmentSkippedTime(client, endUser).doFinally(() -> redirectToAuthorize(routingContext)).subscribe();
    } else {
        FactorProvider provider = optFactor.get().getValue();
        if (provider.checkSecurityFactor(getSecurityFactor(params, optFactor.get().getKey()))) {
            // save enrolled factor for the current user and continue
            routingContext.session().put(ConstantKeys.ENROLLED_FACTOR_ID_KEY, factorId);
            if (sharedSecret != null) {
                routingContext.session().put(ConstantKeys.ENROLLED_FACTOR_SECURITY_VALUE_KEY, sharedSecret);
            }
            if (phoneNumber != null) {
                routingContext.session().put(ConstantKeys.ENROLLED_FACTOR_PHONE_NUMBER, phoneNumber);
            }
            if (emailAddress != null) {
                routingContext.session().put(ConstantKeys.ENROLLED_FACTOR_EMAIL_ADDRESS, emailAddress);
            }
            redirectToAuthorize(routingContext);
        } else {
            // parameters are invalid
            routingContext.fail(400);
        }
    }
}
Also used : HttpHeaders(io.gravitee.common.http.HttpHeaders) Client(io.gravitee.am.model.oidc.Client) TemplateEngine(io.vertx.reactivex.ext.web.common.template.TemplateEngine) LoggerFactory(org.slf4j.LoggerFactory) UserService(io.gravitee.am.gateway.handler.root.service.user.UserService) ConstantKeys(io.gravitee.am.common.utils.ConstantKeys) FactorSecurityType(io.gravitee.am.common.factor.FactorSecurityType) EnrolledFactorChannel(io.gravitee.am.model.factor.EnrolledFactorChannel) AbstractEndpoint(io.gravitee.am.gateway.handler.root.resources.endpoint.AbstractEndpoint) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) Map(java.util.Map) Type(io.gravitee.am.model.factor.EnrolledFactorChannel.Type) Observable(io.reactivex.Observable) AsyncResult(io.vertx.core.AsyncResult) RequestUtils(io.gravitee.am.gateway.handler.common.vertx.utils.RequestUtils) Logger(org.slf4j.Logger) HttpServerRequest(io.vertx.reactivex.core.http.HttpServerRequest) Optional.ofNullable(java.util.Optional.ofNullable) Future(io.vertx.core.Future) HttpServerResponse(io.vertx.reactivex.core.http.HttpServerResponse) RoutingContext(io.vertx.reactivex.ext.web.RoutingContext) Collectors(java.util.stream.Collectors) io.gravitee.am.model(io.gravitee.am.model) EnrolledFactorSecurity(io.gravitee.am.model.factor.EnrolledFactorSecurity) ThymeleafDataHelper.generateData(io.gravitee.am.gateway.handler.common.utils.ThymeleafDataHelper.generateData) UriBuilderRequest(io.gravitee.am.gateway.handler.common.vertx.utils.UriBuilderRequest) MultiMap(io.vertx.reactivex.core.MultiMap) List(java.util.List) CONTEXT_PATH(io.gravitee.am.gateway.handler.common.vertx.utils.UriBuilderRequest.CONTEXT_PATH) Optional(java.util.Optional) 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) FactorProvider(io.gravitee.am.factor.api.FactorProvider) MultiMap(io.vertx.reactivex.core.MultiMap) EnrolledFactor(io.gravitee.am.model.factor.EnrolledFactor) io.gravitee.am.model(io.gravitee.am.model) Client(io.gravitee.am.model.oidc.Client)

Aggregations

ConstantKeys (io.gravitee.am.common.utils.ConstantKeys)2 Enrollment (io.gravitee.am.factor.api.Enrollment)2 FactorProvider (io.gravitee.am.factor.api.FactorProvider)2 FactorManager (io.gravitee.am.gateway.handler.common.factor.FactorManager)2 EnrolledFactor (io.gravitee.am.model.factor.EnrolledFactor)2 EnrolledFactorChannel (io.gravitee.am.model.factor.EnrolledFactorChannel)2 Type (io.gravitee.am.model.factor.EnrolledFactorChannel.Type)2 EnrolledFactorSecurity (io.gravitee.am.model.factor.EnrolledFactorSecurity)2 Client (io.gravitee.am.model.oidc.Client)2 Observable (io.reactivex.Observable)2 AsyncResult (io.vertx.core.AsyncResult)2 Future (io.vertx.core.Future)2 Handler (io.vertx.core.Handler)2 RoutingContext (io.vertx.reactivex.ext.web.RoutingContext)2 Collectors (java.util.stream.Collectors)2 InvalidFactorAttributeException (io.gravitee.am.common.exception.mfa.InvalidFactorAttributeException)1 InvalidRequestException (io.gravitee.am.common.exception.oauth2.InvalidRequestException)1 FactorDataKeys (io.gravitee.am.common.factor.FactorDataKeys)1 FactorSecurityType (io.gravitee.am.common.factor.FactorSecurityType)1 RECOVERY_CODE (io.gravitee.am.common.factor.FactorSecurityType.RECOVERY_CODE)1