no.digipost.api.useragreements.client.DigipostUserAgreementsClient.java Source code

Java tutorial

Introduction

Here is the source code for no.digipost.api.useragreements.client.DigipostUserAgreementsClient.java

Source

/**
 * Copyright (C) Posten Norge AS
 *
 * 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 no.digipost.api.useragreements.client;

import no.digipost.api.useragreements.client.filters.request.RequestContentSHA256Filter;
import no.digipost.api.useragreements.client.filters.request.RequestDateInterceptor;
import no.digipost.api.useragreements.client.filters.request.RequestSignatureInterceptor;
import no.digipost.api.useragreements.client.filters.request.RequestUserAgentInterceptor;
import no.digipost.api.useragreements.client.filters.response.ResponseDateInterceptor;
import no.digipost.api.useragreements.client.response.StreamingRateLimitedResponse;
import no.digipost.api.useragreements.client.security.CryptoUtil;
import no.digipost.api.useragreements.client.security.PrivateKeySigner;
import no.digipost.http.client3.DigipostHttpClientFactory;
import no.digipost.http.client3.DigipostHttpClientSettings;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.ResponseHandler;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;

import java.io.InputStream;
import java.net.URI;
import java.security.PrivateKey;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static no.digipost.api.useragreements.client.response.ResponseUtils.isOkResponse;
import static no.digipost.api.useragreements.client.response.ResponseUtils.mapOkResponseOrThrowException;
import static no.digipost.api.useragreements.client.response.ResponseUtils.readErrorEntity;
import static no.digipost.api.useragreements.client.response.ResponseUtils.unmarshallEntity;

/**
 * API client for managing Digipost documents on behalf of users
 */
public class DigipostUserAgreementsClient {

    static {
        CryptoUtil.addBouncyCastleProviderAndVerify_AES256_CBC_Support();
    }

    private final ApiService apiService;

    public DigipostUserAgreementsClient(final ApiService apiService) {
        this.apiService = apiService;
        CryptoUtil.verifyTLSCiphersAvailable();
    }

    public IdentificationResult identifyUser(final SenderId senderId, final UserId userId) {
        return identifyUser(senderId, userId, null);
    }

    public IdentificationResult identifyUser(final SenderId senderId, final UserId userId,
            final String requestTrackingId) {
        Objects.requireNonNull(senderId, "senderId cannot be null");
        Objects.requireNonNull(userId, "userId cannot be null");
        return apiService.identifyUser(senderId, userId, requestTrackingId,
                singleJaxbEntityHandler(IdentificationResult.class));
    }

    public void createOrReplaceAgreement(final SenderId senderId, final Agreement agreement) {
        createOrReplaceAgreement(senderId, agreement, null);
    }

    public void createOrReplaceAgreement(final SenderId senderId, final Agreement agreement,
            final String requestTrackingId) {
        Objects.requireNonNull(senderId, "senderId cannot be null");
        Objects.requireNonNull(agreement, "agreement cannot be null");
        apiService.createAgreement(senderId, agreement, requestTrackingId, voidOkHandler());
    }

    public GetAgreementResult getAgreement(final SenderId senderId, final AgreementType type, final UserId userId) {
        return getAgreement(senderId, type, userId, null);
    }

    public GetAgreementResult getAgreement(final SenderId senderId, final AgreementType type, final UserId userId,
            final String requestTrackingId) {
        Objects.requireNonNull(senderId, "senderId cannot be null");
        Objects.requireNonNull(type, "agreementType cannot be null");
        Objects.requireNonNull(userId, "userId cannot be null");
        return apiService.getAgreement(senderId, type, userId, requestTrackingId, response -> {
            StatusLine status = response.getStatusLine();
            if (isOkResponse(status.getStatusCode())) {
                return new GetAgreementResult(unmarshallEntity(response, Agreement.class));
            } else {
                final Error error = readErrorEntity(response);
                if (status.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                    if (error.hasCode(ErrorCode.UNKNOWN_USER_ID)) {
                        return new GetAgreementResult(GetAgreementResult.FailedReason.UNKNOWN_USER,
                                () -> new UnexpectedResponseException(status, error));
                    } else if (error.hasCode(ErrorCode.AGREEMENT_NOT_FOUND)) {
                        return new GetAgreementResult(GetAgreementResult.FailedReason.NO_AGREEMENT,
                                () -> new UnexpectedResponseException(status, error));
                    }
                }
                throw new UnexpectedResponseException(status, error);
            }
        });
    }

    public List<Agreement> getAgreements(final SenderId senderId, final UserId userId) {
        return getAgreements(senderId, userId, null);
    }

    public List<Agreement> getAgreements(final SenderId senderId, final UserId userId,
            final String requestTrackingId) {
        return apiService
                .getAgreements(senderId, userId, requestTrackingId, singleJaxbEntityHandler(Agreements.class))
                .getAgreements();
    }

    public void deleteAgreement(final SenderId senderId, final AgreementType agreementType, final UserId userId) {
        deleteAgreement(senderId, agreementType, userId, null);
    }

    public void deleteAgreement(final SenderId senderId, final AgreementType agreementType, final UserId userId,
            final String requestTrackingId) {
        apiService.deleteAgrement(senderId, agreementType, userId, requestTrackingId, voidOkHandler());
    }

    public List<Document> getDocuments(final SenderId senderId, final AgreementType agreementType,
            final UserId userId, final GetDocumentsQuery query) {
        return getDocuments(senderId, agreementType, userId, query, null);
    }

    public List<Document> getDocuments(final SenderId senderId, final AgreementType agreementType,
            final UserId userId, final GetDocumentsQuery query, final String requestTrackingId) {
        Objects.requireNonNull(senderId, "senderId cannot be null");
        Objects.requireNonNull(agreementType, "agreementType cannot be null");
        Objects.requireNonNull(userId, "userId cannot be null");
        final Documents documents = apiService.getDocuments(senderId, agreementType, userId, query,
                requestTrackingId, singleJaxbEntityHandler(Documents.class));
        return documents.getDocuments();
    }

    public Document getDocument(final SenderId senderId, final AgreementType agreementType, final long documentId) {
        return getDocument(senderId, agreementType, documentId, null);
    }

    public Document getDocument(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final String requestTrackingId) {
        return apiService.getDocument(senderId, agreementType, documentId, requestTrackingId,
                singleJaxbEntityHandler(Document.class));
    }

    public void payInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final InvoicePayment invoicePayment) {
        payInvoice(senderId, agreementType, documentId, invoicePayment, null);
    }

    public void payInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final InvoicePayment invoicePayment, final String requestTrackingId) {
        apiService.updateInvoice(senderId, agreementType, documentId, invoicePayment.asInvoiceUpdate(),
                requestTrackingId, voidOkHandler());
    }

    public void updateInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final InvoiceUpdate invoiceUpdate) {
        updateInvoice(senderId, agreementType, documentId, invoiceUpdate, null);
    }

    public void updateInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final InvoiceUpdate invoiceUpdate, final String requestTrackingId) {
        apiService.updateInvoice(senderId, agreementType, documentId, invoiceUpdate, requestTrackingId,
                voidOkHandler());
    }

    public void deleteInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId) {
        deleteInvoice(senderId, agreementType, documentId, null);
    }

    public void deleteInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final String requestTrackingId) {
        apiService.updateInvoice(senderId, agreementType, documentId, new InvoiceUpdate(InvoiceStatus.DELETED),
                requestTrackingId, voidOkHandler());
    }

    public long getDocumentCount(final SenderId senderId, final AgreementType agreementType, final UserId userId,
            final GetDocumentsQuery query) {
        return getDocumentCount(senderId, agreementType, userId, query, null);
    }

    public long getDocumentCount(final SenderId senderId, final AgreementType agreementType, final UserId userId,
            final GetDocumentsQuery query, final String requestTrackingId) {
        Objects.requireNonNull(senderId, "senderId cannot be null");
        Objects.requireNonNull(agreementType, "agreementType cannot be null");
        Objects.requireNonNull(userId, "userId cannot be null");
        return apiService.getDocumentCount(senderId, agreementType, userId, query, requestTrackingId,
                singleJaxbEntityHandler(DocumentCount.class)).getCount();
    }

    public DocumentContent getDocumentContent(final SenderId senderId, final AgreementType agreementType,
            final long documentId) {
        return getDocumentContent(senderId, agreementType, documentId, null);
    }

    public DocumentContent getDocumentContent(final SenderId senderId, final AgreementType agreementType,
            final long documentId, final String requestTrackingId) {
        return apiService.getDocumentContent(senderId, agreementType, documentId, requestTrackingId,
                singleJaxbEntityHandler(DocumentContent.class));
    }

    public StreamingRateLimitedResponse<UserId> getAgreementOwners(final SenderId senderId,
            final AgreementType agreementType) {
        return getAgreementOwners(senderId, agreementType, null);
    }

    public StreamingRateLimitedResponse<UserId> getAgreementOwners(final SenderId senderId,
            final AgreementType agreementType, final Boolean smsNotificationEnabled) {
        return getAgreementOwners(senderId, agreementType, smsNotificationEnabled, null);
    }

    public StreamingRateLimitedResponse<UserId> getAgreementOwners(final SenderId senderId,
            final AgreementType agreementType, final Boolean smsNotificationEnabled,
            final String requestTrackingId) {
        Objects.requireNonNull(senderId, "senderId cannot be null");
        Objects.requireNonNull(agreementType, "agreementType cannot be null");
        return apiService.getAgreementOwners(senderId, agreementType, smsNotificationEnabled, requestTrackingId);
    }

    private ResponseHandler<Void> voidOkHandler() {
        return response -> mapOkResponseOrThrowException(response, r -> null);
    }

    private <T> ResponseHandler<T> singleJaxbEntityHandler(Class<T> responseType) {
        return response -> mapOkResponseOrThrowException(response, r -> unmarshallEntity(r, responseType));
    }

    public static class Builder {

        private static final URI PRODUCTION_ENDPOINT = URI.create("https://api.digipost.no");

        private URI serviceEndpoint;
        private final BrokerId brokerId;
        private final InputStream certificateP12File;
        private final String certificatePassword;
        private final Optional<PrivateKey> privateKey;
        private HttpClientBuilder httpClientBuilder;
        private Optional<HttpHost> proxyHost = Optional.empty();

        public Builder(final BrokerId brokerId, InputStream certificateP12File, String certificatePassword) {
            this(brokerId, certificateP12File, certificatePassword, null);
        }

        public Builder(BrokerId brokerId, PrivateKey privateKey) {
            this(brokerId, null, null, privateKey);
        }

        private Builder(BrokerId brokerId, InputStream certificateP12File, String certificatePassword,
                PrivateKey privateKey) {
            this.brokerId = brokerId;
            if (privateKey == null && (certificateP12File == null || certificatePassword == null)) {
                throw new IllegalArgumentException(
                        "Client must be supplied either PrivateKey, or Certificate and password for certificate");
            }
            this.certificateP12File = certificateP12File;
            this.certificatePassword = certificatePassword;
            this.privateKey = Optional.ofNullable(privateKey);
            serviceEndpoint(PRODUCTION_ENDPOINT);
            httpClientBuilder = DigipostHttpClientFactory.createBuilder(DigipostHttpClientSettings.DEFAULT);
        }

        public Builder useProxy(final HttpHost proxyHost) {
            this.proxyHost = Optional.ofNullable(proxyHost);
            return this;
        }

        public Builder serviceEndpoint(URI endpointUri) {
            this.serviceEndpoint = endpointUri;
            return this;
        }

        public Builder setHttpClientBuilder(final HttpClientBuilder httpClientBuilder) {
            this.httpClientBuilder = httpClientBuilder;
            return this;
        }

        public Builder veryDangerouslyDisableCertificateVerificationWhichIsAbsolutelyUnfitForProductionCode() {
            if (this.serviceEndpoint.compareTo(PRODUCTION_ENDPOINT) == 0) {
                throw new RuntimeException(
                        "You should never ever disable certificate verification when connecting to the production endpoint");
            }
            SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
            try {
                sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
                SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                        sslContextBuilder.build(), (hostname, session) -> true);
                httpClientBuilder.setSSLSocketFactory(sslConnectionSocketFactory);
            } catch (Exception e) {
                throw new RuntimeException("Could not disable certificate verification: " + e.getMessage(), e);
            }
            System.err.println("Not checking validity of certificates for any hostnames");
            return this;
        }

        public DigipostUserAgreementsClient build() {
            CryptoUtil.addBouncyCastleProviderAndVerify_AES256_CBC_Support();

            httpClientBuilder.addInterceptorLast(new RequestDateInterceptor());
            httpClientBuilder.addInterceptorLast(new RequestUserAgentInterceptor());
            PrivateKeySigner pkSigner = privateKey.map(PrivateKeySigner::new)
                    .orElseGet(() -> new PrivateKeySigner(certificateP12File, certificatePassword));
            httpClientBuilder.addInterceptorLast(
                    new RequestSignatureInterceptor(pkSigner, new RequestContentSHA256Filter()));
            httpClientBuilder.addInterceptorLast(new ResponseDateInterceptor());
            proxyHost.ifPresent(httpClientBuilder::setProxy);

            ApiService apiService = new ApiService(serviceEndpoint, brokerId, httpClientBuilder.build());
            return new DigipostUserAgreementsClient(apiService);
        }
    }
}