datadome_fraud_sdk_ruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +2 -0
- data/lib/constants.rb +2 -0
- data/lib/datadome_fraud_sdk_ruby.rb +123 -0
- data/lib/model/address.rb +33 -0
- data/lib/model/api/request.rb +82 -0
- data/lib/model/api/response.rb +84 -0
- data/lib/model/events.rb +52 -0
- data/lib/model/operation.rb +6 -0
- data/lib/model/session.rb +19 -0
- data/lib/model/user.rb +40 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 562e73467299bca33309183c1e8f84e26571346082000ca19767295d4bcdb558
|
4
|
+
data.tar.gz: 9f60b87c715a36bc402d76018b9b96f7d30a901d8f1e07b6488f7537c2251f87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7163115049e8696367c168e69a36a1fa36f80f6bb64e72baeaab93c9614136feefe89954ec9e415913d51db72eb152670f91e2dac7f5223c5ddc225286d43979
|
7
|
+
data.tar.gz: 6600c8180c52163a5039c47d4ae5895142e57b63a602ae13bef327e4a8137e393a64540bdc00e2bcf8a549b38640c9915e77beb3cc75495ab7ab7a514300eafc
|
data/README.md
ADDED
data/lib/constants.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "json"
|
5
|
+
require_relative "model/events"
|
6
|
+
require_relative "model/user"
|
7
|
+
require_relative "model/session"
|
8
|
+
require_relative "model/operation"
|
9
|
+
require_relative "model/api/request"
|
10
|
+
require_relative "model/api/response"
|
11
|
+
|
12
|
+
class DataDome
|
13
|
+
attr_accessor :timeout, :endpoint, :logger
|
14
|
+
|
15
|
+
def initialize(timeout = 1500, endpoint = "account-api.datadome.co", logger = Logger.new(STDOUT))
|
16
|
+
@timeout = timeout
|
17
|
+
@endpoint = endpoint
|
18
|
+
@logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate(request, event)
|
22
|
+
if event.status == StatusType::UNDEFINED
|
23
|
+
event.status = StatusType::SUCCEEDED
|
24
|
+
end
|
25
|
+
|
26
|
+
payload = build_payload(request, event)
|
27
|
+
|
28
|
+
begin
|
29
|
+
api_response = sendRequest(OperationType::VALIDATE, event.action, payload)
|
30
|
+
|
31
|
+
begin
|
32
|
+
body = JSON.parse(api_response.body) unless api_response.body.nil?
|
33
|
+
rescue JSON::ParserError => e
|
34
|
+
@logger.error("DataDome: error parsing JSON from Fraud API #{e.message}")
|
35
|
+
return DataDomeResponseError.new(DataDomeResponseAction::ALLOW, DataDomeResponseStatus::FAILURE, "DataDome: error parsing JSON from Fraud API", e.message)
|
36
|
+
end
|
37
|
+
|
38
|
+
if api_response.success?
|
39
|
+
datadome_response = DataDomeResponseSuccess.new(DataDomeResponseAction::ALLOW, DataDomeResponseStatus::OK, body["reasons"], body["ip"], body["location"])
|
40
|
+
if body["action"] === DataDomeResponseAction::DENY
|
41
|
+
datadome_response.action = DataDomeResponseAction::DENY
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@logger.error("DataDome: error on Fraud API response #{body["errors"]}")
|
45
|
+
return DataDomeResponseError.new(DataDomeResponseAction::ALLOW, DataDomeResponseStatus::FAILURE, body["message"], body["errors"])
|
46
|
+
end
|
47
|
+
rescue Faraday::TimeoutError => e
|
48
|
+
return DataDomeResponseError.new(DataDomeResponseAction::ALLOW, DataDomeResponseStatus::TIMEOUT, "DataDome Fraud API request timeout", e.message)
|
49
|
+
rescue Faraday::ConnectionFailed => e
|
50
|
+
return DataDomeResponseError.new(DataDomeResponseAction::ALLOW, DataDomeResponseStatus::FAILURE, "DataDome Fraud API request failed", e.message)
|
51
|
+
end
|
52
|
+
|
53
|
+
datadome_response
|
54
|
+
end
|
55
|
+
|
56
|
+
def collect(request, event)
|
57
|
+
if event.status == StatusType::UNDEFINED
|
58
|
+
event.status = StatusType::FAILED
|
59
|
+
end
|
60
|
+
|
61
|
+
payload = build_payload(request, event)
|
62
|
+
|
63
|
+
begin
|
64
|
+
api_response = sendRequest(OperationType::COLLECT, event.action, payload)
|
65
|
+
|
66
|
+
if api_response.success?
|
67
|
+
datadome_response = DataDomeResponseSuccess.new(nil, DataDomeResponseStatus::OK, nil, nil, nil)
|
68
|
+
else
|
69
|
+
begin
|
70
|
+
body = JSON.parse(api_response.body)
|
71
|
+
@logger.error("DataDome: error on Fraud API response #{body["errors"]}")
|
72
|
+
datadome_response = DataDomeResponseError.new(nil, DataDomeResponseStatus::FAILURE, body["message"], body["errors"])
|
73
|
+
rescue JSON::ParserError => e
|
74
|
+
@logger.error("DataDome: error parsing JSON from Fraud API #{e.message}")
|
75
|
+
return DataDomeResponseError.new(nil, DataDomeResponseStatus::FAILURE, "DataDome: error parsing JSON from Fraud API", e.message)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue Faraday::TimeoutError => e
|
79
|
+
datadome_response = DataDomeResponseError.new(nil, DataDomeResponseStatus::TIMEOUT, "DataDome Fraud API request timeout", e.message)
|
80
|
+
rescue Faraday::ConnectionFailed => e
|
81
|
+
datadome_response = DataDomeResponseError.new(nil, DataDomeResponseStatus::FAILURE, "DataDome Fraud API request failed", e.message)
|
82
|
+
end
|
83
|
+
|
84
|
+
datadome_response
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def sendRequest(operation, event, payload)
|
90
|
+
api_response = client.post("/v1/" + operation + "/" + event) do |req|
|
91
|
+
req.body = payload.to_json
|
92
|
+
end
|
93
|
+
api_response
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_payload(request, event)
|
97
|
+
metadata = event.merge_with(DataDomeRequest.new(request))
|
98
|
+
payload = {}
|
99
|
+
metadata.instance_variables.each do |var|
|
100
|
+
key = var.to_s.delete("@") # Remove '@' from variable name to get key
|
101
|
+
payload[key] = metadata.instance_variable_get(var)
|
102
|
+
end
|
103
|
+
payload
|
104
|
+
end
|
105
|
+
|
106
|
+
def client
|
107
|
+
Faraday.new(api_uri) do |req|
|
108
|
+
req.adapter Faraday.default_adapter
|
109
|
+
req.options[:timeout] = @timeout / 1000.0 #timeout in seconds
|
110
|
+
req.headers["accept"] = "application/json"
|
111
|
+
req.headers["content-type"] = "application/json"
|
112
|
+
req.headers["x-api-key"] = ENV["DATADOME_FRAUD_API_KEY"]
|
113
|
+
req.ssl.verify = true if api_uri.scheme == "https"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def api_uri
|
118
|
+
url = @endpoint
|
119
|
+
url = "https://#{url}" unless (url.downcase.start_with?("https://") || url.include?("://"))
|
120
|
+
|
121
|
+
URI(url)
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Address
|
4
|
+
attr_accessor :name, :line1, :line2, :city, :countryCode, :country, :regionCode, :zipCode
|
5
|
+
|
6
|
+
def initialize(name = nil, line1 = nil, line2 = nil, city = nil, countryCode = nil, country = nil, regionCode = nil, zipCode = nil)
|
7
|
+
@name = name
|
8
|
+
@line1 = line1
|
9
|
+
@line2 = line2
|
10
|
+
@city = city
|
11
|
+
@countryCode = countryCode
|
12
|
+
@country = country
|
13
|
+
@regionCode = regionCode
|
14
|
+
@zipCode = zipCode
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"Address: name=#{name}, line1=#{line1}, line2=#{line2}, city=#{city}, countryCode=#{countryCode}, country=#{country}, regionCode=#{regionCode}, zipCode=#{zipCode}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json(options = {})
|
22
|
+
{
|
23
|
+
name: name,
|
24
|
+
line1: line1,
|
25
|
+
line2: line2,
|
26
|
+
city: city,
|
27
|
+
countryCode: countryCode,
|
28
|
+
country: country,
|
29
|
+
regionCode: regionCode,
|
30
|
+
zipCode: zipCode,
|
31
|
+
}.to_json
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../constants"
|
4
|
+
|
5
|
+
class DatadomeModule
|
6
|
+
attr_accessor :requestTimeMicros, :name, :version
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@requestTimeMicros = Time.now.strftime("%s%6N").to_i
|
10
|
+
@name = "Fraud SDK Ruby"
|
11
|
+
@version = SDK_VERSION
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_json(options = {})
|
15
|
+
{
|
16
|
+
requestTimeMicros: @requestTimeMicros,
|
17
|
+
name: @name,
|
18
|
+
version: @version,
|
19
|
+
}.to_json
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class DataDomeHeaders
|
24
|
+
attr_accessor :addr, :clientIp, :contentype, :host, :port, :xRealIP, :xForwardedForIp,
|
25
|
+
:acceptEncoding, :acceptLanguage, :accept, :method, :protocol, :serverHostname,
|
26
|
+
:referer, :userAgent, :from, :request, :origin, :acceptCharset, :connection,
|
27
|
+
:clientId, :secCHUA, :secCHUAMobile, :secCHUAPlatform, :secCHUAArch,
|
28
|
+
:secCHUAFullVersionList, :secCHUAModel, :secCHDeviceMemory
|
29
|
+
|
30
|
+
def initialize(request)
|
31
|
+
@addr = request.remote_ip
|
32
|
+
@clientIp = request.headers["HTTP_CLIENT_IP"]
|
33
|
+
@contentype = truncate(request.headers["Content-Type"], 64)
|
34
|
+
@host = truncate(request.headers["HTTP_HOST"], 512)
|
35
|
+
@port = request.port || 0
|
36
|
+
@xRealIP = truncate(request.headers["HTTP_X_REAL_IP"], 128)
|
37
|
+
@xForwardedForIp = truncate(request.headers["HTTP_X_FORWARDED_FOR"], -512)
|
38
|
+
@acceptEncoding = truncate(request.headers["HTTP_ACCEPT_ENCODING"], 128)
|
39
|
+
@acceptLanguage = truncate(request.headers["HTTP_ACCEPT_LANGUAGE"], 256)
|
40
|
+
@accept = truncate(request.headers["HTTP_ACCEPT"], 512)
|
41
|
+
@method = request.method
|
42
|
+
@protocol = request.protocol.gsub("://", "")
|
43
|
+
@serverHostname = truncate(request.headers["HTTP_HOST"], 256)
|
44
|
+
@referer = truncate(request.headers["HTTP_REFERER"], 1024)
|
45
|
+
@userAgent = truncate(request.headers["HTTP_USER_AGENT"], 768)
|
46
|
+
@from = truncate(request.headers["HTTP_FROM"], 128)
|
47
|
+
@request = [request.path, request.query_string].reject(&:blank?).join("?")[0, 2048]
|
48
|
+
@origin = truncate(request.headers["HTTP_ORIGIN"], 512)
|
49
|
+
@acceptCharset = truncate(request.headers["HTTP_ACCEPT_CHARSET"], 128)
|
50
|
+
@connection = truncate(request.headers["HTTP_CONNECTION"], 128)
|
51
|
+
@clientId = request.cookies["datadome"]
|
52
|
+
@secCHUA = truncate(request.headers["HTTP_SEC_CH_UA"], 128)
|
53
|
+
@secCHUAMobile = truncate(request.headers["HTTP_SEC_CH_UA_MOBILE"], 8)
|
54
|
+
@secCHUAPlatform = truncate(request.headers["HTTP_SEC_CH_UA_PLATFORM"], 32)
|
55
|
+
@secCHUAArch = truncate(request.headers["HTTP_SEC_CH_UA_ARCH"], 16)
|
56
|
+
@secCHUAFullVersionList = truncate(request.headers["HTTP_SEC_CH_UA_FULL_VERSION_LIST"], 256)
|
57
|
+
@secCHUAModel = truncate(request.headers["HTTP_SEC_CH_UA_MODEL"], 128)
|
58
|
+
@secCHDeviceMemory = truncate(request.headers["HTTP_SEC_CH_DEVICE_MEMORY"], 8)
|
59
|
+
end
|
60
|
+
|
61
|
+
def truncate(value, max_length = nil)
|
62
|
+
return value if value.nil? || max_length.nil?
|
63
|
+
|
64
|
+
max_length < 0 ? value[max_length] : value[0, max_length]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class DataDomeRequest
|
69
|
+
attr_accessor :module, :header
|
70
|
+
|
71
|
+
def initialize(request)
|
72
|
+
@module = DatadomeModule.new
|
73
|
+
@header = DataDomeHeaders.new(request)
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_json(options = {})
|
77
|
+
{
|
78
|
+
module: @module.to_json,
|
79
|
+
header: @header.to_json,
|
80
|
+
}.to_json
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DataDomeResponseAction
|
4
|
+
ALLOW = "allow"
|
5
|
+
DENY = "deny"
|
6
|
+
end
|
7
|
+
|
8
|
+
module DataDomeResponseStatus
|
9
|
+
OK = "ok"
|
10
|
+
FAILURE = "failure"
|
11
|
+
TIMEOUT = "timeout"
|
12
|
+
end
|
13
|
+
|
14
|
+
class Error
|
15
|
+
attr_accessor :field, :error
|
16
|
+
end
|
17
|
+
|
18
|
+
class DataDomeResponse
|
19
|
+
attr_accessor :action, :status
|
20
|
+
|
21
|
+
def initialize(action, status)
|
22
|
+
@action = action
|
23
|
+
@status = status
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"DataDomeResponse: action=#{action}, status=#{status}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_json(options = {})
|
31
|
+
{
|
32
|
+
action: @action,
|
33
|
+
status: @status,
|
34
|
+
}.to_json
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class DataDomeResponseSuccess < DataDomeResponse
|
39
|
+
attr_accessor :ip, :reasons, :location
|
40
|
+
|
41
|
+
def initialize(action, status, reasons, ip, location)
|
42
|
+
super(action, status)
|
43
|
+
@reasons = reasons
|
44
|
+
@ip = ip
|
45
|
+
@location = location
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"DataDomeResponseSuccess: action=#{action}, status=#{status}, reasons=#{reasons}, ip=#{ip}, location=#{location}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_json(options = {})
|
53
|
+
{
|
54
|
+
action: @action,
|
55
|
+
status: @status,
|
56
|
+
reasons: @reasons,
|
57
|
+
ip: @ip,
|
58
|
+
location: @location,
|
59
|
+
}.to_json
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class DataDomeResponseError < DataDomeResponse
|
64
|
+
attr_accessor :message, :errors
|
65
|
+
|
66
|
+
def initialize(action, status, message, errors)
|
67
|
+
super(action, status)
|
68
|
+
@message = message
|
69
|
+
@errors = errors
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"DataDomeResponseError: action=#{action}, status=#{status}, message=#{message}, errors=#{errors}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_json(options = {})
|
77
|
+
{
|
78
|
+
action: @action,
|
79
|
+
status: @status,
|
80
|
+
message: @message,
|
81
|
+
errors: @errors,
|
82
|
+
}.to_json
|
83
|
+
end
|
84
|
+
end
|
data/lib/model/events.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionType
|
4
|
+
LOGIN = "login"
|
5
|
+
REGISTER = "registration"
|
6
|
+
PAYMENT = "payment"
|
7
|
+
end
|
8
|
+
|
9
|
+
module StatusType
|
10
|
+
SUCCEEDED = "succeeded"
|
11
|
+
FAILED = "failed"
|
12
|
+
UNDEFINED = "undefined"
|
13
|
+
end
|
14
|
+
|
15
|
+
class DataDomeEvent
|
16
|
+
attr_accessor :action, :status, :account
|
17
|
+
|
18
|
+
def initialize(action, account, status = StatusType::UNDEFINED)
|
19
|
+
@action = action
|
20
|
+
@account = account
|
21
|
+
@status = status
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge_with(request_data)
|
25
|
+
request_data.instance_variable_set(:@account, @account)
|
26
|
+
request_data.instance_variable_set(:@status, @status)
|
27
|
+
request_data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class LoginEvent < DataDomeEvent
|
32
|
+
def initialize(account, status = StatusType::UNDEFINED)
|
33
|
+
super(ActionType::LOGIN, account, status)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class RegistrationEvent < DataDomeEvent
|
38
|
+
attr_accessor :user, :session
|
39
|
+
|
40
|
+
def initialize(account, user, session = nil, status = StatusType::UNDEFINED)
|
41
|
+
super(ActionType::REGISTER, account, status)
|
42
|
+
@session = session
|
43
|
+
@user = user
|
44
|
+
end
|
45
|
+
|
46
|
+
def merge_with(request_data)
|
47
|
+
super(request_data)
|
48
|
+
request_data.instance_variable_set(:@session, @session)
|
49
|
+
request_data.instance_variable_set(:@user, @user)
|
50
|
+
request_data
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DataDomeSession
|
4
|
+
def initialize(id, createdAt = Time.now.strftime("%s%6N").to_i)
|
5
|
+
@id = id
|
6
|
+
@createdAt = createdAt
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
"DataDomeSession: id=#{id}, createdAt =#{createdAt}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_json(options = {})
|
14
|
+
{
|
15
|
+
id: id,
|
16
|
+
createdAt: createdAt,
|
17
|
+
}.to_json
|
18
|
+
end
|
19
|
+
end
|
data/lib/model/user.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
4
|
+
require_relative "./address"
|
5
|
+
|
6
|
+
module Title
|
7
|
+
EMPTY = nil
|
8
|
+
MR = "mr"
|
9
|
+
MRS = "mrs"
|
10
|
+
MX = "mx"
|
11
|
+
end
|
12
|
+
|
13
|
+
class DataDomeUser
|
14
|
+
def initialize(id, title = nil, firstName = nil, lastName = nil, createdAt = Time.now.iso8601, phone = nil, email = nil, address = nil)
|
15
|
+
@id = id
|
16
|
+
@title = title
|
17
|
+
@firstName = firstName
|
18
|
+
@lastName = lastName
|
19
|
+
@createdAt = createdAt
|
20
|
+
@phone = phone
|
21
|
+
@email = email
|
22
|
+
@address = address
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"DataDomeUser: id=#{id}, title =#{title}, firstname =#{firstName}, lastname =#{lastName} createdAt =#{createdAt}, phone =#{phone}, address= #{address}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json(options = {})
|
30
|
+
{
|
31
|
+
id: @id,
|
32
|
+
firstName: @firstName,
|
33
|
+
lastName: @lastName,
|
34
|
+
createdAt: @createdAt,
|
35
|
+
phone: @phone,
|
36
|
+
email: @email,
|
37
|
+
address: @address,
|
38
|
+
}.to_json
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: datadome_fraud_sdk_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- DataDome
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-04-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
description: DataDome Fraud Protection - Ruby SDK
|
28
|
+
email: support@datadome.co
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/constants.rb
|
35
|
+
- lib/datadome_fraud_sdk_ruby.rb
|
36
|
+
- lib/model/address.rb
|
37
|
+
- lib/model/api/request.rb
|
38
|
+
- lib/model/api/response.rb
|
39
|
+
- lib/model/events.rb
|
40
|
+
- lib/model/operation.rb
|
41
|
+
- lib/model/session.rb
|
42
|
+
- lib/model/user.rb
|
43
|
+
homepage: https://datadome.co/
|
44
|
+
licenses:
|
45
|
+
- Apache-2.0
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubygems_version: 3.4.19
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: DataDome Account Protect detects account takeover threats and protects you
|
66
|
+
against them.
|
67
|
+
test_files: []
|