001/*
002 *   Copyright 2024 Vonage
003 *
004 *   Licensed under the Apache License, Version 2.0 (the "License");
005 *   you may not use this file except in compliance with the License.
006 *   You may obtain a copy of the License at
007 *
008 *        http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *   Unless required by applicable law or agreed to in writing, software
011 *   distributed under the License is distributed on an "AS IS" BASIS,
012 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *   See the License for the specific language governing permissions and
014 *   limitations under the License.
015 */
016package com.vonage.client.camara.simswap;
017
018import com.vonage.client.DynamicEndpoint;
019import com.vonage.client.HttpWrapper;
020import com.vonage.client.RestEndpoint;
021import com.vonage.client.VonageClient;
022import com.vonage.client.auth.camara.BackendAuthRequest;
023import com.vonage.client.auth.camara.NetworkAuthMethod;
024import com.vonage.client.auth.camara.FraudPreventionDetectionScope;
025import static com.vonage.client.auth.camara.FraudPreventionDetectionScope.CHECK_SIM_SWAP;
026import static com.vonage.client.auth.camara.FraudPreventionDetectionScope.RETRIEVE_SIM_SWAP_DATE;
027import com.vonage.client.camara.CamaraResponseException;
028import com.vonage.client.camara.NetworkApiClient;
029import com.vonage.client.common.HttpMethod;
030import java.time.Instant;
031
032/**
033 * A client for communicating with the Vonage SIM Swap API. The standard way to obtain an instance
034 * of this class is to use {@link VonageClient#getSimSwapClient()}.
035 */
036public class SimSwapClient extends NetworkApiClient {
037    final RestEndpoint<SimSwapRequest, CheckSimSwapResponse> check;
038    final RestEndpoint<SimSwapRequest, SimSwapDateResponse> retrieveDate;
039
040    /**
041     * Create a new SimSwapClient.
042     *
043     * @param wrapper Http Wrapper used to create requests.
044     */
045    public SimSwapClient(HttpWrapper wrapper) {
046        super(wrapper);
047
048        @SuppressWarnings("unchecked")
049        class Endpoint<R> extends DynamicEndpoint<SimSwapRequest, R> {
050            Endpoint(String path, FraudPreventionDetectionScope scope, R... type) {
051                super(DynamicEndpoint.<SimSwapRequest, R> builder(type)
052                        .authMethod(NetworkAuthMethod.class)
053                        .responseExceptionType(CamaraResponseException.class)
054                        .requestMethod(HttpMethod.POST).wrapper(wrapper).pathGetter((de, req) -> {
055                            setNetworkAuth(new BackendAuthRequest(req.getPhoneNumber(), scope));
056                            return getCamaraBaseUri() + "sim-swap/v040/" + path;
057                        })
058                );
059            }
060        }
061
062        check = new Endpoint<>("check", CHECK_SIM_SWAP);
063        retrieveDate = new Endpoint<>("retrieve-date", RETRIEVE_SIM_SWAP_DATE);
064    }
065
066    private boolean checkSimSwap(SimSwapRequest request) {
067        return check.execute(request).getSwapped();
068    }
069
070    /**
071     * Check if SIM swap has been performed within the last 240 hours for the given phone number.
072     * Use {@linkplain #checkSimSwap(String, int)} to specify the time period.
073     *
074     * @param phoneNumber Subscriber number in E.164 format (starting with country code). Optionally prefixed with '+'.
075     *
076     * @return {@code true} if the SIM card has been swapped during the period within the provided age.
077     *
078     * @throws CamaraResponseException If the request was unsuccessful. This could be for the following reasons:
079     * <ul>
080     *     <li><b>400</b>: Invalid request arguments.</li>
081     *     <li><b>401</b>: Request not authenticated due to missing, invalid, or expired credentials.</li>
082     *     <li><b>403</b>: Client does not have sufficient permissions to perform this action.</li>
083     *     <li><b>404</b>: SIM Swap can't be checked because the phone number is unknown.</li>
084     *     <li><b>409</b>: Another request is created for the same MSISDN.</li>
085     *     <li><b>502</b>: Bad gateway.</li>
086     * </ul>
087     */
088    public boolean checkSimSwap(String phoneNumber) {
089        return checkSimSwap(new SimSwapRequest(phoneNumber));
090    }
091
092    /**
093     * Check if SIM swap has been performed during the specified past period for the given phone number.
094     *
095     * @param phoneNumber Subscriber number in E.164 format (starting with country code). Optionally prefixed with '+'.
096     * @param maxAgeHours Period in hours to be checked for SIM swap. Must be between 1 and 2400.
097     *
098     * @return {@code true} if the SIM card has been swapped during the period within the provided age.
099     *
100     * @throws CamaraResponseException If the request was unsuccessful. This could be for the following reasons:
101     * <ul>
102     *     <li><b>400</b>: Invalid request arguments.</li>
103     *     <li><b>401</b>: Request not authenticated due to missing, invalid, or expired credentials.</li>
104     *     <li><b>403</b>: Client does not have sufficient permissions to perform this action.</li>
105     *     <li><b>404</b>: SIM Swap can't be checked because the phone number is unknown.</li>
106     *     <li><b>409</b>: Another request is created for the same MSISDN.</li>
107     *     <li><b>502</b>: Bad gateway.</li>
108     * </ul>
109     */
110    public boolean checkSimSwap(String phoneNumber, int maxAgeHours) {
111        return checkSimSwap(new SimSwapRequest(phoneNumber, maxAgeHours));
112    }
113
114    /**
115     * Get timestamp of last MSISDN to IMSI pairing change for a mobile user account.
116     *
117     * @param phoneNumber Subscriber number in E.164 format (starting with country code). Optionally prefixed with '+'.
118     *
119     * @return Time of the latest SIM swap performed, or {@code null} if unknown / not applicable.
120     *
121     * @throws CamaraResponseException If the request was unsuccessful. This could be for the following reasons:
122         * <ul>
123     *     <li><b>400</b>: Invalid request arguments.</li>
124     *     <li><b>401</b>: Request not authenticated due to missing, invalid, or expired credentials.</li>
125     *     <li><b>403</b>: Client does not have sufficient permissions to perform this action.</li>
126     *     <li><b>404</b>: SIM Swap can't be checked because the phone number is unknown.</li>
127         *     <li><b>409</b>: Another request is created for the same MSISDN.</li>
128         *     <li><b>502</b>: Bad gateway.</li>
129         * </ul>
130     */
131    public Instant retrieveSimSwapDate(String phoneNumber) {
132        return retrieveDate.execute(new SimSwapRequest(phoneNumber)).getLatestSimChange();
133    }
134}