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}