All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gravitee.am.authdevice.notifier.http.provider.HttpAuthenticationDeviceNotifierProvider Maven / Gradle / Ivy

There is a newer version: 4.6.0-alpha.2
Show newest version
/**
 * 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.am.authdevice.notifier.http.provider;

import io.gravitee.am.authdevice.notifier.api.AuthenticationDeviceNotifierProvider;
import io.gravitee.am.authdevice.notifier.api.exception.DeviceNotificationException;
import io.gravitee.am.authdevice.notifier.api.model.ADCallbackContext;
import io.gravitee.am.authdevice.notifier.api.model.ADNotificationRequest;
import io.gravitee.am.authdevice.notifier.api.model.ADNotificationResponse;
import io.gravitee.am.authdevice.notifier.api.model.ADUserResponse;
import io.gravitee.am.authdevice.notifier.http.HttpAuthenticationDeviceNotifierConfiguration;
import io.gravitee.am.authdevice.notifier.http.provider.spring.HttpAuthenticationDeviceProviderSpringConfiguration;
import io.gravitee.common.http.HttpStatusCode;
import io.reactivex.rxjava3.core.Single;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonObject;
import io.vertx.rxjava3.core.MultiMap;
import io.vertx.rxjava3.core.buffer.Buffer;
import io.vertx.rxjava3.ext.web.client.HttpRequest;
import io.vertx.rxjava3.ext.web.client.WebClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Import;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Optional;

import static org.springframework.util.StringUtils.isEmpty;

/**
 * @author Eric LELEU (eric.leleu at graviteesource.com)
 * @author GraviteeSource Team
 */
@Import(HttpAuthenticationDeviceProviderSpringConfiguration.class)
public class HttpAuthenticationDeviceNotifierProvider implements AuthenticationDeviceNotifierProvider {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpAuthenticationDeviceNotifierProvider.class);

    public static final String TRANSACTION_ID = "tid";
    public static final String STATE = "state";
    public static final String PARAM_SUBJECT = "subject";
    public static final String PARAM_SCOPE = "scope";
    public static final String PARAM_EXPIRE = "expire";
    public static final String PARAM_ACR = "acr";
    public static final String PARAM_MESSAGE = "message";
    public static final String RESPONSE_ATTR_DATA = "data";
    public static final String CALLBACK_VALIDATE = "validated";

    public static final String URL_TO_GET_FROM_DOMAIN_SETTINGS = System.getProperty("ciba.ades", "http://localhost:8080/ciba/ades");

    @Autowired
    @Qualifier("authDeviceNotifierWebClient")
    private WebClient client;

    @Autowired
    private HttpAuthenticationDeviceNotifierConfiguration configuration;

    @Override
    public Single notify(ADNotificationRequest request) {
        LOGGER.debug("Call notifier service '{}' (tid: {}) ", this.configuration.getEndpoint(), request.getTransactionId());
        final MultiMap formData = MultiMap.caseInsensitiveMultiMap();

        formData.set(TRANSACTION_ID, request.getTransactionId());
        formData.set(STATE, request.getState());
        formData.set(PARAM_SUBJECT, request.getSubject());
        formData.set(PARAM_SCOPE, request.getScopes());
        formData.set(PARAM_EXPIRE, Integer.toString(request.getExpiresIn()));

        if (!CollectionUtils.isEmpty(request.getAcrValues())) {
            formData.set(PARAM_ACR, request.getAcrValues());
        }

        if (!isEmpty(request.getMessage())) {
            formData.set(PARAM_MESSAGE, request.getMessage());
        }

        final HttpRequest notificationRequest = this.client.requestAbs(HttpMethod.POST, this.configuration.getEndpoint());
        if (!StringUtils.isEmpty(this.configuration.getHeaderValue())) {
            notificationRequest
                    .putHeader(this.configuration.getHeaderName(), this.configuration.getHeaderValue());
        }

        return notificationRequest
                .rxSendForm(formData)
                .doOnError((error) -> LOGGER.warn("Unexpected error during device notification : {}", error.getMessage(), error))
                .onErrorResumeNext(exception -> Single.error(new DeviceNotificationException("Unexpected error during device notification")))
                .flatMap(response -> {
                    final int status = response.statusCode();
                    if (status != HttpStatusCode.OK_200) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Device notification fails on '{}' for tid '{}' with status '{}': {}",
                                    this.configuration.getEndpoint(),
                                    request.getTransactionId(),
                                    status,
                                    response.bodyAsString());
                        } else {
                            LOGGER.info("Device notification fails for tid '{}' with status '{}'", request.getTransactionId(), status);
                        }
                        
                        return Single.error(new DeviceNotificationException("Device notification fails"));
                    }

                    final JsonObject result = response.bodyAsJsonObject();
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Notifier '{}' respond with status '{}' for tid '{}': {}",
                                this.configuration.getEndpoint(),
                                status,
                                request.getTransactionId(),
                                result);
                    }

                    if ( !request.getTransactionId().equals(result.getString(TRANSACTION_ID)) || !request.getState().equals(result.getString(STATE))) {
                        LOGGER.warn("Device notification response contains invalid tid or state", request.getTransactionId(), status);
                        return Single.error(new DeviceNotificationException("Invalid device notification response"));
                    }

                    final ADNotificationResponse notificationResponse = new ADNotificationResponse(request.getTransactionId());
                    final JsonObject extraData = result.getJsonObject(RESPONSE_ATTR_DATA);
                    if (extraData != null) {
                        notificationResponse.setExtraData(extraData.getMap());
                    }
                    return Single.just(notificationResponse);
                });
    }

    @Override
    public Single> extractUserResponse(ADCallbackContext callbackContext) {
        final String state = callbackContext.getParam(STATE);
        final String tid = callbackContext.getParam(TRANSACTION_ID);
        final String validated = callbackContext.getParam(CALLBACK_VALIDATE);
        LOGGER.debug("User response received: state={}, tid={}, validated={}", state, tid, validated);
        if (isEmpty(state) || isEmpty(tid) || isEmpty(validated)) {
            return Single.just(Optional.empty());
        } else {
            return Single.just(Optional.of(new ADUserResponse(tid, state, Boolean.valueOf(validated))));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy