diff --git a/galoy.yaml b/galoy.yaml index 4560a38983..d6ffa2a1ae 100644 --- a/galoy.yaml +++ b/galoy.yaml @@ -143,9 +143,9 @@ spamLimits: memoSharingSatsThreshold: 1000 memoSharingCentsThreshold: 50 ipRecording: - enabled: true + enabled: false proxyChecking: - enabled: true + enabled: false fees: withdraw: method: flat diff --git a/src/app/accounts/update-account-ip.ts b/src/app/accounts/update-account-ip.ts index 1ab7466be5..c4a23680ca 100644 --- a/src/app/accounts/update-account-ip.ts +++ b/src/app/accounts/update-account-ip.ts @@ -1,9 +1,13 @@ import { getIpConfig } from "@config" import { RepositoryError } from "@domain/errors" import { IpFetcherServiceError } from "@domain/ipfetcher" +import { ErrorLevel } from "@domain/shared" import { IpFetcher } from "@services/ipfetcher" import { AccountsIpRepository } from "@services/mongoose/accounts-ips" -import { asyncRunInSpan, SemanticAttributes } from "@services/tracing" +import { + addAttributesToCurrentSpan, + recordExceptionInCurrentSpan, +} from "@services/tracing" const accountsIp = AccountsIpRepository() @@ -15,67 +19,102 @@ export const updateAccountIPsInfo = async ({ accountId: AccountId ip?: IpAddress logger: Logger -}): Promise => - asyncRunInSpan( - "app.users.updateAccountIPsInfo", - { - attributes: { - [SemanticAttributes.CODE_FUNCTION]: "updateAccountIPsInfo", - [SemanticAttributes.CODE_NAMESPACE]: "app.users", - }, - }, - async () => { - const ipConfig = getIpConfig() - - const lastConnection = new Date() - - const userIP = await accountsIp.findById(accountId) - if (userIP instanceof RepositoryError) return userIP - - if (!ip || !ipConfig.ipRecordingEnabled) { - const result = await accountsIp.update(userIP) - - if (result instanceof Error) { - logger.error( - { result, accountId, ip }, - "impossible to update user last connection", - ) - - return result - } - - return - } - - const lastIP = userIP.lastIPs.find((ipObject) => ipObject.ip === ip) - - if (lastIP) { - lastIP.lastConnection = lastConnection - } else { - let ipInfo: IPType = { +}): Promise => { + const ipConfig = getIpConfig() + + const lastConnection = new Date() + + const accountIP = await accountsIp.findById(accountId) + if (accountIP instanceof RepositoryError) return accountIP + + if (!ip || !ipConfig.ipRecordingEnabled) { + const result = await accountsIp.update(accountIP) + + if (result instanceof Error) { + logger.error( + { result, accountId, ip }, + "impossible to update account last connection", + ) + + return result + } + + return + } + + let ipInfo: IPType + + const ipFromDb = accountIP.lastIPs.find((ipObject) => ipObject.ip === ip) + + if (ipFromDb) { + ipInfo = ipFromDb + ipInfo.lastConnection = lastConnection + } else { + ipInfo = { + ip, + firstConnection: lastConnection, + lastConnection: lastConnection, + } + } + + if ( + ipConfig.proxyCheckingEnabled && + (!ipInfo.isoCode || !ipInfo.proxy || !ipInfo.asn) + ) { + console.log({ ipInfo }, "ipInfo") + + const ipFetcher = IpFetcher() + const ipFetcherInfo = await ipFetcher.fetchIPInfo(ip) + + if (ipFetcherInfo instanceof IpFetcherServiceError) { + recordExceptionInCurrentSpan({ + error: ipFetcherInfo.message, + level: ErrorLevel.Warn, + attributes: { + ip, + accountId, + }, + }) + + logger.error({ accountId, ip }, "impossible to get ip detail") + return ipFetcherInfo + } + + // deep copy + const ipFetcherInfoForOtel = JSON.parse(JSON.stringify(ipFetcherInfo)) + + for (const key in ipFetcherInfoForOtel) { + ipFetcherInfoForOtel["proxycheck." + key] = ipFetcherInfoForOtel[key] + delete ipFetcherInfoForOtel[key] + } + + addAttributesToCurrentSpan(ipFetcherInfoForOtel) + + if (!ipFetcherInfo.isoCode || !ipFetcherInfo.proxy || !ipFetcherInfo.asn) { + const error = `missing mandatory fields. isoCode: ${ipFetcherInfo.isoCode}, proxy: ${ipFetcherInfo.proxy}, asn: ${ipFetcherInfo.asn}` + recordExceptionInCurrentSpan({ + error, + level: ErrorLevel.Warn, + attributes: { ip, - firstConnection: lastConnection, - lastConnection: lastConnection, - } - - if (ipConfig.proxyCheckingEnabled) { - const ipFetcher = IpFetcher() - const ipFetcherInfo = await ipFetcher.fetchIPInfo(ip) - - if (ipFetcherInfo instanceof IpFetcherServiceError) { - logger.error({ accountId, ip }, "impossible to get ip detail") - return ipFetcherInfo - } - - ipInfo = { ...ipInfo, ...ipFetcherInfo } - } - userIP.lastIPs.push(ipInfo) - } - const result = await accountsIp.update(userIP) - - if (result instanceof Error) { - logger.error({ result, accountId, ip }, "impossible to update ip") - return result - } - }, - ) + accountId, + }, + }) + } else { + // using Object.assign instead of ... because of conflict with mongoose hidden properties + ipInfo = Object.assign(ipInfo, ipFetcherInfo) + + // removing current ip from lastIPs - if it exists + accountIP.lastIPs = accountIP.lastIPs.filter((ipDb) => ipDb.ip !== ip) + + // adding it back with the correct info + accountIP.lastIPs.push(ipInfo) + } + } + const result = await accountsIp.update(accountIP) + + if (result instanceof Error) { + logger.error({ result, accountId, ip }, "impossible to update ip") + return result + } +} diff --git a/src/app/auth/request-phone-code.ts b/src/app/auth/request-phone-code.ts index 1081a12454..7294198809 100644 --- a/src/app/auth/request-phone-code.ts +++ b/src/app/auth/request-phone-code.ts @@ -1,5 +1,6 @@ -import { getTestAccounts } from "@config" +import { getTestAccounts, getTwilioConfig } from "@config" import { TestAccountsChecker } from "@domain/accounts/test-accounts-checker" +import { NotImplementedError } from "@domain/errors" import { RateLimitConfig } from "@domain/rate-limit" import { RateLimiterExceededError } from "@domain/rate-limit/errors" import { consumeLimiter } from "@services/rate-limit" @@ -69,6 +70,10 @@ export const requestPhoneCode = async ({ return true } + if (getTwilioConfig().accountSid === "AC_twilio_id") { + return new NotImplementedError("use test account for local dev and tests") + } + return TwilioClient().initiateVerify(phone) } diff --git a/src/config/schema.ts b/src/config/schema.ts index 1afba20687..b28e07dd71 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -452,9 +452,9 @@ export const configSchema = { required: ["enabled"], additionalProperties: false, default: { - enabled: true, + enabled: false, proxyChecking: { - enabled: true, + enabled: false, }, }, }, diff --git a/src/config/yaml.ts b/src/config/yaml.ts index 93a71fc258..addae98483 100644 --- a/src/config/yaml.ts +++ b/src/config/yaml.ts @@ -264,9 +264,8 @@ export const getBuildVersions = (): { export const PROXY_CHECK_APIKEY = yamlConfig?.PROXY_CHECK_APIKEY export const getIpConfig = (config = yamlConfig): IpConfig => ({ - ipRecordingEnabled: - process.env.NODE_ENV === "test" ? false : config.ipRecording?.enabled, - proxyCheckingEnabled: config.ipRecording?.proxyChecking?.enabled, + ipRecordingEnabled: config.ipRecording.enabled, + proxyCheckingEnabled: config.ipRecording.proxyChecking.enabled, }) export const getApolloConfig = (config = yamlConfig): ApolloConfig => config.apollo diff --git a/src/services/ipfetcher/index.ts b/src/services/ipfetcher/index.ts index 682b40902c..a4cec4886d 100644 --- a/src/services/ipfetcher/index.ts +++ b/src/services/ipfetcher/index.ts @@ -1,24 +1,29 @@ import axios from "axios" import { UnknownIpFetcherServiceError } from "@domain/ipfetcher" import { PROXY_CHECK_APIKEY } from "@config" -import { addAttributesToCurrentSpan } from "@services/tracing" export const IpFetcher = (): IIpFetcherService => { const fetchIPInfo = async (ip: string): Promise => { try { - const { data } = await axios.get( - `https://proxycheck.io/v2/${ip}?key=${PROXY_CHECK_APIKEY}&vpn=1&asn=1`, - ) - const proxy = !!(data[ip] && data[ip].proxy && data[ip].proxy === "yes") - const isoCode = data[ip] && data[ip].isocode + const params: { [id: string]: string } = { + vpn: "1", + asn: "1", + time: "1", + risk: "1", + } + + if (PROXY_CHECK_APIKEY) { + params["key"] = PROXY_CHECK_APIKEY + } - addAttributesToCurrentSpan({ - ...data[ip], - isoCode, - proxy, - status: data.status, + const { data } = await axios.request({ + url: `https://proxycheck.io/v2/${ip}`, + params, }) + const proxy = !!(data[ip] && data[ip].proxy && data[ip].proxy === "yes") + const isoCode = data[ip] && data[ip].isocode + return { ...data[ip], isoCode, proxy, status: data.status } } catch (err) { return new UnknownIpFetcherServiceError(err)