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

io.getlime.security.powerauth.lib.tpp.engine.client.TppEngineClient Maven / Gradle / Ivy

The newest version!
/*
 * PowerAuth Web Flow and related software components
 * Copyright (C) 2019 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 .
 */

package io.getlime.security.powerauth.lib.tpp.engine.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.wultra.core.rest.client.base.DefaultRestClient;
import com.wultra.core.rest.client.base.RestClient;
import com.wultra.core.rest.client.base.RestClientConfiguration;
import com.wultra.core.rest.client.base.RestClientException;
import io.getlime.core.rest.model.base.entity.Error;
import io.getlime.core.rest.model.base.request.ObjectRequest;
import io.getlime.core.rest.model.base.response.ObjectResponse;
import io.getlime.core.rest.model.base.response.Response;
import io.getlime.security.powerauth.app.tppengine.model.entity.TppEngineError;
import io.getlime.security.powerauth.app.tppengine.model.request.CreateTppAppRequest;
import io.getlime.security.powerauth.app.tppengine.model.request.GiveConsentRequest;
import io.getlime.security.powerauth.app.tppengine.model.request.RemoveConsentRequest;
import io.getlime.security.powerauth.app.tppengine.model.response.ConsentDetailResponse;
import io.getlime.security.powerauth.app.tppengine.model.response.GiveConsentResponse;
import io.getlime.security.powerauth.app.tppengine.model.response.TppAppDetailResponse;
import io.getlime.security.powerauth.app.tppengine.model.response.UserConsentDetailResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.Collections;
import java.util.List;

/**
 * TPP Engine Client provides methods for communication with the TPP registry and consent engine.
 * It uses the Rest Client to handle REST API calls.
 *
 * @author Petr Dvorak, [email protected]
 * @author Roman Strobl, [email protected]
 */
public class TppEngineClient {

    private static final Logger logger = LoggerFactory.getLogger(TppEngineClient.class);

    private final RestClient restClient;

    /**
     * Create a new client with provided base URL.
     *
     * @param serviceUrl Base service URL.
     * @throws TppEngineClientException Thrown when REST client initialization fails.
     */
    public TppEngineClient(String serviceUrl) throws TppEngineClientException {
        try {
            final RestClientConfiguration.JacksonConfiguration jacksonConfiguration = new RestClientConfiguration.JacksonConfiguration();
            jacksonConfiguration.getSerialization().put(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            jacksonConfiguration.getDeserialization().put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            RestClientConfiguration config = new RestClientConfiguration();
            config.setBaseUrl(serviceUrl);
            config.setJacksonConfiguration(jacksonConfiguration);
            restClient = new DefaultRestClient(config);
        } catch (RestClientException ex) {
            TppEngineClientException ex2 = new TppEngineClientException(ex, new TppEngineError(Error.Code.ERROR_GENERIC, "Rest client initialization failed."));
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Create a new client with detailed configuration of REST client.
     *
     * @param restClientConfiguration REST service client configuration.
     * @throws TppEngineClientException Thrown when REST client initialization fails.
     */
    public TppEngineClient(RestClientConfiguration restClientConfiguration) throws TppEngineClientException {
        try {
            restClient = new DefaultRestClient(restClientConfiguration);
        } catch (RestClientException ex) {
            TppEngineClientException ex2 = new TppEngineClientException(ex, new TppEngineError(Error.Code.ERROR_GENERIC, "Rest client initialization failed."));
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Lookup consent details (details of a consent template).
     *
     * @param id Username for user account which is being looked up.
     * @return Response with user details.
     * @throws TppEngineClientException Thrown when client request fails or user does not exist.
     */
    public ObjectResponse consentDetail(String id) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("id", Collections.singletonList(id));
        return getObjectImpl("/consent", params, ConsentDetailResponse.class);
    }

    /**
     * Lookup consent status for given user and app.
     *
     * @param userId Username for user account which is being looked up.
     * @param consentId Identifier of a consent.
     * @param clientId Identifier of a TPP app.
     * @return Response with details of consent for given user and TPP app.
     * @throws TppEngineClientException Thrown when client request fails or user does not exist.
     */
    public ObjectResponse consentStatus(String userId, String consentId, String clientId) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("userId", Collections.singletonList(userId));
        params.put("consentId", Collections.singletonList(consentId));
        params.put("clientId", Collections.singletonList(clientId));
        return getObjectImpl("/user/consent/status", params, UserConsentDetailResponse.class);
    }

    /**
     * Give consent according to request parameters.
     *
     * @param request Consent information.
     * @return Information about newly created consent.
     * @throws TppEngineClientException Thrown when client request fails or authentication fails.
     */
    public ObjectResponse giveConsent(GiveConsentRequest request) throws TppEngineClientException {
        return postObjectImpl("/user/consent", new ObjectRequest<>(request), GiveConsentResponse.class);
    }

    /**
     * Reject consent according to request parameters.
     *
     * @param request Consent information.
     * @return Static OK response. In case consent didn't exist, this operation is no-op.
     * @throws TppEngineClientException Thrown when client request fails or authentication fails.
     */
    public Response rejectConsent(RemoveConsentRequest request) throws TppEngineClientException {
        return postObjectImpl("/user/consent/delete", new ObjectRequest<>(request));
    }

    /**
     * Lookup information about a provided app.
     *
     * @param clientId Identifier of a TPP app.
     * @return Response with details TPP app with given client ID.
     * @throws TppEngineClientException Thrown when client request fails or app does not exist.
     */
    public ObjectResponse fetchAppInfo(String clientId) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("clientId", Collections.singletonList(clientId));
        return getObjectImpl("/tpp/app", params, TppAppDetailResponse.class);
    }

    /**
     * Lookup information about a provided app.
     *
     * @param clientId Identifier of a TPP app.
     * @param tppLicense TPP license information.
     * @return Response with details TPP app with given client ID.
     * @throws TppEngineClientException Thrown when client request fails or app does not exist.
     */
    public ObjectResponse fetchAppInfoWithLicenseRestriction(String clientId, String tppLicense) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("clientId", Collections.singletonList(clientId));
        params.put("tppLicense", Collections.singletonList(tppLicense));
        return getObjectImpl("/tpp/app", params, TppAppDetailResponse.class);
    }

    /**
     * Fetch list of TPP applications based on the license info.
     *
     * @param tppLicense TPP license information.
     * @return Response with details TPP app with given client ID.
     * @throws TppEngineClientException Thrown when client request fails or app does not exist.
     */
    public ObjectResponse> fetchApplicationList(String tppLicense) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("tppLicense", Collections.singletonList(tppLicense));
        return getImpl("/tpp/app/list", params, new ParameterizedTypeReference<>(){});
    }

    /**
     * Create an application with provided information.
     *
     * @param request New application information.
     * @return Information about newly created application.
     * @throws TppEngineClientException Thrown when client request fails or user does not exist.
     */
    public ObjectResponse createApplication(CreateTppAppRequest request) throws TppEngineClientException {
        return postObjectImpl("/tpp/app", new ObjectRequest<>(request), TppAppDetailResponse.class);
    }

    /**
     * Update an application with provided client ID with a new information.
     *
     * @param clientId Client ID of an app to be updated.
     * @param request New application information.
     * @return Information about newly created application.
     * @throws TppEngineClientException Thrown when client request fails or user does not exist.
     */
    public ObjectResponse updateApplication(String clientId, CreateTppAppRequest request) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("clientId", Collections.singletonList(clientId));
        return putObjectImpl("/tpp/app", new ObjectRequest<>(request), params, TppAppDetailResponse.class);
    }

    /**
     * Renew the client secret for the application.
     *
     * @param clientId Client ID of an application to be refreshed.
     * @param tppLicense License information of TPP.
     * @return Information about newly created application.
     * @throws TppEngineClientException Thrown when client request fails or user does not exist.
     */
    public ObjectResponse renewClientSecret(String clientId, String tppLicense) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("clientId", Collections.singletonList(clientId));
        params.put("tppLicense", Collections.singletonList(tppLicense));
        return postObjectImpl("/tpp/app/renewSecret", null, params, TppAppDetailResponse.class);
    }

    /**
     * Delete an application with provided information.
     *
     * @param clientId Client ID of the app.
     * @param tppLicense License information of TPP.
     * @return Generic response.
     * @throws TppEngineClientException Thrown when client request fails or user does not exist.
     */
    public Response deleteApplication(String clientId, String tppLicense) throws TppEngineClientException {
        final MultiValueMap params = new LinkedMultiValueMap<>();
        params.put("clientId", Collections.singletonList(clientId));
        params.put("tppLicense", Collections.singletonList(tppLicense));
        return deleteObjectImpl("/tpp/app", params);
    }

    // Generic HTTP client methods

    /**
     * Prepare a generic GET response.
     *
     * @param path Resource path.
     * @param typeReference Type reference.
     * @param queryParams Query parameters.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private  T getImpl(String path, MultiValueMap queryParams, ParameterizedTypeReference typeReference) throws TppEngineClientException {
        try {
            return restClient.get(path, queryParams, null, typeReference).getBody();
        } catch (RestClientException ex) {
            final TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Prepare GET object response.
     *
     * @param path Resource path.
     * @param queryParams Query parameters.
     * @param responseType Response type.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private  ObjectResponse getObjectImpl(String path, MultiValueMap queryParams, Class responseType) throws TppEngineClientException {
        try {
            return restClient.getObject(path, queryParams, null, responseType);
        } catch (RestClientException ex) {
            final TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Prepare POST object response. Uses default {@link Response} type reference for response.
     *
     * @param path Resource path.
     * @param request Request body.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private Response postObjectImpl(String path, ObjectRequest request) throws TppEngineClientException {
        try {
            return restClient.postObject(path, request);
        } catch (RestClientException ex) {
            final TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Prepare POST object response.
     *
     * @param path Resource path.
     * @param request Request body.
     * @param responseType Response type.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private  ObjectResponse postObjectImpl(String path, ObjectRequest request, Class responseType) throws TppEngineClientException {
        try {
            return restClient.postObject(path, request, responseType);
        } catch (RestClientException ex) {
            TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Prepare POST object response.
     *
     * @param path Resource path.
     * @param request Request body.
     * @param queryParams Query parameters.
     * @param responseType Response type.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private  ObjectResponse postObjectImpl(String path, ObjectRequest request, MultiValueMap queryParams, Class responseType) throws TppEngineClientException {
        try {
            return restClient.postObject(path, request, queryParams, null, responseType);
        } catch (RestClientException ex) {
            final TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Prepare PUT object response.
     *
     * @param path Resource path.
     * @param request Request body.
     * @param queryParams Query parameters.
     * @param responseType Response type.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private  ObjectResponse putObjectImpl(String path, ObjectRequest request, MultiValueMap queryParams, Class responseType) throws TppEngineClientException {
        try {
            return restClient.putObject(path, request, queryParams, null, responseType);
        } catch (RestClientException ex) {
            final TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Prepare a generic DELETE response.
     *
     * @param path Resource path.
     * @param queryParams Query parameters.
     * @return Object obtained after processing the response JSON.
     * @throws TppEngineClientException In case of network, response / JSON processing, or other IO error.
     */
    private Response deleteObjectImpl(String path, MultiValueMap queryParams) throws TppEngineClientException {
        try {
            return restClient.deleteObject(path, queryParams, null);
        } catch (RestClientException ex) {
            final TppEngineClientException ex2 = createTppEngineClientException(ex);
            logError(ex2);
            throw ex2;
        }
    }

    /**
     * Log TPP engine client exception details.
     * @param ex TPP engine client exception.
     */
    private void logError(TppEngineClientException ex) {
        Error error = ex.getError();
        if (error != null) {
            logger.warn("TPP Engine REST API call failed with error code: {}", error.getCode());
        } else {
            logger.warn(ex.getMessage(), ex);
        }
    }

    private static TppEngineClientException createTppEngineClientException(final RestClientException e) {
        return new TppEngineClientException(e, resolveTppEngineError(e));
    }

    private static TppEngineError resolveTppEngineError(final RestClientException e) {
        // TODO (racansky, 2022-12-02) workaround until https://github.com/wultra/lime-java-core/issues/32
        if (e.getErrorResponse() == null || e.getErrorResponse().getResponseObject() == null) {
            logger.trace("Wultra Java Core lib did not parse ErrorResponse for {}", e.getResponse());
            try {
                final ObjectResponse errorResponse = new ObjectMapper().readValue(e.getResponse(), new TypeReference<>(){});
                if (errorResponse != null && errorResponse.getResponseObject() != null) {
                    return errorResponse.getResponseObject();
                }
            } catch (JsonProcessingException ex2) {
                logger.debug("Problem to deserialize error response", ex2);
            }
            return new TppEngineError(resolveErrorCode(e), e.getMessage());
        }

        final Error error = e.getErrorResponse().getResponseObject();
        return new TppEngineError(error.getCode(), error.getMessage());
    }

    /**
     * Resolve error code based on HTTP status code from REST client exception.
     */
    private static String resolveErrorCode(RestClientException ex) {
        if (ex.getStatusCode() == null) {
            // REST client errors, response not received
            return Error.Code.ERROR_GENERIC;
        }
        if (ex.getStatusCode().is4xxClientError()) {
            // Errors caused by invalid TPP engine client requests
            return TppEngineError.Code.TPP_ENGINE_CLIENT_ERROR;
        }
        if (ex.getStatusCode().is5xxServerError()) {
            // Internal errors in TPP engine
            return TppEngineError.Code.REMOTE_ERROR;
        }
        // Other errors during communication
        return TppEngineError.Code.COMMUNICATION_ERROR;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy