geetest_ruby_sdk 4.0.3

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.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module Geetest
6
+ module V4
7
+ class Account
8
+ DEFAULT_API_SERVER = 'http://gcaptcha4.geetest.com'
9
+ DIGEST_MOD = 'hmac_sha256'
10
+
11
+ attr_accessor :captcha_id, :captcha_key, :api_server
12
+
13
+ def initialize(captcha_id, captcha_key, **options)
14
+ @captcha_id = captcha_id
15
+ @captcha_key = captcha_key
16
+ @api_server = URI.parse options.fetch(:api_server, DEFAULT_API_SERVER)
17
+ end
18
+
19
+ def validate?(lot_number: nil, captcha_output: nil, pass_token: nil, gen_time: nil, **options)
20
+ return false unless lot_number && captcha_output && pass_token && gen_time
21
+
22
+ payload = {
23
+ lot_number: lot_number,
24
+ captcha_output: captcha_output,
25
+ pass_token: pass_token,
26
+ gen_time: gen_time,
27
+ sign_token: Encryptor.encrypt(lot_number).with(captcha_key).by(DIGEST_MOD).to_s
28
+ }
29
+ response = request! payload, options
30
+ Geetest.logger.debug "Geetest validate failed, reason: #{response['reason']}" if response['result'] != 'success'
31
+ response['result'] == 'success'
32
+ rescue *Geetest.exceptions_to_degraded_mode => e
33
+ Geetest.logger.info "Geetest validate request failed for #{e.message}, fall back to degraded mode"
34
+ true
35
+ end
36
+
37
+ private
38
+
39
+ def request!(payload, options = {})
40
+ response = RestClient.post "#{api_server}/validate?captcha_id=#{captcha_id}", options.merge(payload)
41
+ JSON.parse response.body
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/geetest.rb ADDED
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'rest-client'
5
+
6
+ require 'geetest/encryptor'
7
+
8
+ require 'geetest/v3/concerns/degraded_mode'
9
+ require 'geetest/v3/account'
10
+ require 'geetest/v3/register'
11
+ require 'geetest/v3/validator'
12
+
13
+ require 'geetest/v4/account'
14
+
15
+ module Geetest
16
+ class << self
17
+ attr_writer :logger
18
+
19
+ # Set up a geetest account and keep it in +Geetest.channels+
20
+ # @param [String, Symbol, Hash] channel_or_config
21
+ #
22
+ # @option opts [String] id Geetest CaptchaId
23
+ # @option opts [String] key Geetest CaptchaKey
24
+ # @option opts [String, Symbol] channel (:default) Which channel to store the instance
25
+ # @option opts [String, Symbol] :version (:V3) Which Captcha version to use
26
+ #
27
+ # other available opts depend on which Captcha version used
28
+ # eg. digest_mod, api_server
29
+ #
30
+ # Set up by a hash config:
31
+ # Geetest.setup(id: 'geetest_id', key: 'geetest_key')
32
+ # Geetest.channel
33
+ #
34
+ # Set up by a hash config with channel name:
35
+ # Geetest.setup(id: 'geetest_id', key: 'geetest_key', channel: 'login')
36
+ # Geetest.channel(:login)
37
+ #
38
+ # Set up a channel by another way:
39
+ # Geetest.setup('login').with(id: 'geetest_id', key: 'geetest_key')
40
+ # Geetest.channel(:login)
41
+ #
42
+ def setup(channel_or_config)
43
+ if channel_or_config.is_a? Hash
44
+ build_channel(channel_or_config)
45
+ else
46
+ Object.new.tap do |unready_channel|
47
+ unready_channel.define_singleton_method(:with) { |opts| Geetest.setup opts.merge(channel: channel_or_config) }
48
+ end
49
+ end
50
+ end
51
+
52
+ def build_channel(config)
53
+ config = config.transform_keys(&:to_sym)
54
+ channel_name = config.delete(:channel) || :default
55
+ captcha_version = config.delete(:version) || :V3
56
+ captcha_id = config.delete(:id)
57
+ captcha_key = config.delete(:key)
58
+ account_klass = Geetest.const_get("#{captcha_version.upcase}::Account", false)
59
+ channels[channel_name.to_sym] = account_klass.new(captcha_id, captcha_key, **config)
60
+ end
61
+
62
+ def channel(channel_name = :default)
63
+ channels[channel_name.to_sym]
64
+ end
65
+
66
+ def channels
67
+ @channels ||= {}
68
+ end
69
+
70
+ def exceptions_to_degraded_mode
71
+ @exceptions_to_degraded_mode ||= [
72
+ ::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET, ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT, and more
73
+ JSON::ParserError,
74
+ RestClient::InternalServerError,
75
+ RestClient::NotFound,
76
+ RestClient::RequestTimeout
77
+ ]
78
+ end
79
+
80
+ def logger
81
+ @logger ||= Logger.new(STDERR)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ module GeetestRubySdk
2
+ VERSION = '4.0.3'
3
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'geetest'
5
+
6
+ module GeetestRubySdk
7
+ extend SingleForwardable
8
+
9
+ delegate [:logger, :logger=, :exceptions_to_degraded_mode] => Geetest
10
+
11
+ # @deprecated Use +Geetest#setup+ instead.
12
+ def self.setup(geetest_id, geetest_key)
13
+ Geetest.setup id: geetest_id, key: geetest_key
14
+ end
15
+
16
+ if defined?(::Rails)
17
+ class Engine < ::Rails::Engine
18
+ # Rails -> use app/assets directory.
19
+
20
+ config.to_prepare do
21
+ Geetest.logger = ::Rails.logger
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,353 @@
1
+ "v0.4.8 Geetest Inc.";
2
+
3
+ (function (window) {
4
+ "use strict";
5
+ if (typeof window === 'undefined') {
6
+ throw new Error('Geetest requires browser environment');
7
+ }
8
+
9
+ var document = window.document;
10
+ var Math = window.Math;
11
+ var head = document.getElementsByTagName("head")[0];
12
+
13
+ function _Object(obj) {
14
+ this._obj = obj;
15
+ }
16
+
17
+ _Object.prototype = {
18
+ _each: function (process) {
19
+ var _obj = this._obj;
20
+ for (var k in _obj) {
21
+ if (_obj.hasOwnProperty(k)) {
22
+ process(k, _obj[k]);
23
+ }
24
+ }
25
+ return this;
26
+ }
27
+ };
28
+
29
+ function Config(config) {
30
+ var self = this;
31
+ new _Object(config)._each(function (key, value) {
32
+ self[key] = value;
33
+ });
34
+ }
35
+
36
+ Config.prototype = {
37
+ api_server: 'api.geetest.com',
38
+ protocol: 'http://',
39
+ typePath: '/gettype.php',
40
+ fallback_config: {
41
+ slide: {
42
+ static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
43
+ type: 'slide',
44
+ slide: '/static/js/geetest.0.0.0.js'
45
+ },
46
+ fullpage: {
47
+ static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
48
+ type: 'fullpage',
49
+ fullpage: '/static/js/fullpage.0.0.0.js'
50
+ }
51
+ },
52
+ _get_fallback_config: function () {
53
+ var self = this;
54
+ if (isString(self.type)) {
55
+ return self.fallback_config[self.type];
56
+ } else if (self.new_captcha) {
57
+ return self.fallback_config.fullpage;
58
+ } else {
59
+ return self.fallback_config.slide;
60
+ }
61
+ },
62
+ _extend: function (obj) {
63
+ var self = this;
64
+ new _Object(obj)._each(function (key, value) {
65
+ self[key] = value;
66
+ })
67
+ }
68
+ };
69
+ var isNumber = function (value) {
70
+ return (typeof value === 'number');
71
+ };
72
+ var isString = function (value) {
73
+ return (typeof value === 'string');
74
+ };
75
+ var isBoolean = function (value) {
76
+ return (typeof value === 'boolean');
77
+ };
78
+ var isObject = function (value) {
79
+ return (typeof value === 'object' && value !== null);
80
+ };
81
+ var isFunction = function (value) {
82
+ return (typeof value === 'function');
83
+ };
84
+ var MOBILE = /Mobi/i.test(navigator.userAgent);
85
+ var pt = MOBILE ? 3 : 0;
86
+
87
+ var callbacks = {};
88
+ var status = {};
89
+
90
+ var nowDate = function () {
91
+ var date = new Date();
92
+ var year = date.getFullYear();
93
+ var month = date.getMonth() + 1;
94
+ var day = date.getDate();
95
+ var hours = date.getHours();
96
+ var minutes = date.getMinutes();
97
+ var seconds = date.getSeconds();
98
+
99
+ if (month >= 1 && month <= 9) {
100
+ month = '0' + month;
101
+ }
102
+ if (day >= 0 && day <= 9) {
103
+ day = '0' + day;
104
+ }
105
+ if (hours >= 0 && hours <= 9) {
106
+ hours = '0' + hours;
107
+ }
108
+ if (minutes >= 0 && minutes <= 9) {
109
+ minutes = '0' + minutes;
110
+ }
111
+ if (seconds >= 0 && seconds <= 9) {
112
+ seconds = '0' + seconds;
113
+ }
114
+ var currentdate = year + '-' + month + '-' + day + " " + hours + ":" + minutes + ":" + seconds;
115
+ return currentdate;
116
+ }
117
+
118
+ var random = function () {
119
+ return parseInt(Math.random() * 10000) + (new Date()).valueOf();
120
+ };
121
+
122
+ var loadScript = function (url, cb) {
123
+ var script = document.createElement("script");
124
+ script.charset = "UTF-8";
125
+ script.async = true;
126
+
127
+ // 对geetest的静态资源添加 crossOrigin
128
+ if ( /static\.geetest\.com/g.test(url)) {
129
+ script.crossOrigin = "anonymous";
130
+ }
131
+
132
+ script.onerror = function () {
133
+ cb(true);
134
+ };
135
+ var loaded = false;
136
+ script.onload = script.onreadystatechange = function () {
137
+ if (!loaded &&
138
+ (!script.readyState ||
139
+ "loaded" === script.readyState ||
140
+ "complete" === script.readyState)) {
141
+
142
+ loaded = true;
143
+ setTimeout(function () {
144
+ cb(false);
145
+ }, 0);
146
+ }
147
+ };
148
+ script.src = url;
149
+ head.appendChild(script);
150
+ };
151
+
152
+ var normalizeDomain = function (domain) {
153
+ // special domain: uems.sysu.edu.cn/jwxt/geetest/
154
+ // return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
155
+ return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
156
+ };
157
+ var normalizePath = function (path) {
158
+ path = path.replace(/\/+/g, '/');
159
+ if (path.indexOf('/') !== 0) {
160
+ path = '/' + path;
161
+ }
162
+ return path;
163
+ };
164
+ var normalizeQuery = function (query) {
165
+ if (!query) {
166
+ return '';
167
+ }
168
+ var q = '?';
169
+ new _Object(query)._each(function (key, value) {
170
+ if (isString(value) || isNumber(value) || isBoolean(value)) {
171
+ q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
172
+ }
173
+ });
174
+ if (q === '?') {
175
+ q = '';
176
+ }
177
+ return q.replace(/&$/, '');
178
+ };
179
+ var makeURL = function (protocol, domain, path, query) {
180
+ domain = normalizeDomain(domain);
181
+
182
+ var url = normalizePath(path) + normalizeQuery(query);
183
+ if (domain) {
184
+ url = protocol + domain + url;
185
+ }
186
+
187
+ return url;
188
+ };
189
+
190
+ var load = function (config, send, protocol, domains, path, query, cb) {
191
+ var tryRequest = function (at) {
192
+
193
+ var url = makeURL(protocol, domains[at], path, query);
194
+ loadScript(url, function (err) {
195
+ if (err) {
196
+ if (at >= domains.length - 1) {
197
+ cb(true);
198
+ // report gettype error
199
+ if (send) {
200
+ config.error_code = 508;
201
+ var url = protocol + domains[at] + path;
202
+ reportError(config, url);
203
+ }
204
+ } else {
205
+ tryRequest(at + 1);
206
+ }
207
+ } else {
208
+ cb(false);
209
+ }
210
+ });
211
+ };
212
+ tryRequest(0);
213
+ };
214
+
215
+
216
+ var jsonp = function (domains, path, config, callback) {
217
+ if (isObject(config.getLib)) {
218
+ config._extend(config.getLib);
219
+ callback(config);
220
+ return;
221
+ }
222
+ if (config.offline) {
223
+ callback(config._get_fallback_config());
224
+ return;
225
+ }
226
+
227
+ var cb = "geetest_" + random();
228
+ window[cb] = function (data) {
229
+ if (data.status == 'success') {
230
+ callback(data.data);
231
+ } else if (!data.status) {
232
+ callback(data);
233
+ } else {
234
+ callback(config._get_fallback_config());
235
+ }
236
+ window[cb] = undefined;
237
+ try {
238
+ delete window[cb];
239
+ } catch (e) {
240
+ }
241
+ };
242
+ load(config, true, config.protocol, domains, path, {
243
+ gt: config.gt,
244
+ callback: cb
245
+ }, function (err) {
246
+ if (err) {
247
+ callback(config._get_fallback_config());
248
+ }
249
+ });
250
+ };
251
+
252
+ var reportError = function (config, url) {
253
+ load(config, false, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
254
+ time: nowDate(),
255
+ captcha_id: config.gt,
256
+ challenge: config.challenge,
257
+ pt: pt,
258
+ exception_url: url,
259
+ error_code: config.error_code
260
+ }, function (err) {})
261
+ }
262
+
263
+ var throwError = function (errorType, config) {
264
+ var errors = {
265
+ networkError: '网络错误',
266
+ gtTypeError: 'gt字段不是字符串类型'
267
+ };
268
+ if (typeof config.onError === 'function') {
269
+ config.onError(errors[errorType]);
270
+ } else {
271
+ throw new Error(errors[errorType]);
272
+ }
273
+ };
274
+
275
+ var detect = function () {
276
+ return window.Geetest || document.getElementById("gt_lib");
277
+ };
278
+
279
+ if (detect()) {
280
+ status.slide = "loaded";
281
+ }
282
+
283
+ window.initGeetest = function (userConfig, callback) {
284
+
285
+ var config = new Config(userConfig);
286
+
287
+ if (userConfig.https) {
288
+ config.protocol = 'https://';
289
+ } else if (!userConfig.protocol) {
290
+ config.protocol = window.location.protocol + '//';
291
+ }
292
+
293
+ // for KFC
294
+ if (userConfig.gt === '050cffef4ae57b5d5e529fea9540b0d1' ||
295
+ userConfig.gt === '3bd38408ae4af923ed36e13819b14d42') {
296
+ config.apiserver = 'yumchina.geetest.com/'; // for old js
297
+ config.api_server = 'yumchina.geetest.com';
298
+ }
299
+
300
+ if(userConfig.gt){
301
+ window.GeeGT = userConfig.gt
302
+ }
303
+
304
+ if(userConfig.challenge){
305
+ window.GeeChallenge = userConfig.challenge
306
+ }
307
+
308
+ if (isObject(userConfig.getType)) {
309
+ config._extend(userConfig.getType);
310
+ }
311
+ jsonp([config.api_server || config.apiserver], config.typePath, config, function (newConfig) {
312
+ var type = newConfig.type;
313
+ var init = function () {
314
+ config._extend(newConfig);
315
+ callback(new window.Geetest(config));
316
+ };
317
+
318
+ callbacks[type] = callbacks[type] || [];
319
+ var s = status[type] || 'init';
320
+ if (s === 'init') {
321
+ status[type] = 'loading';
322
+
323
+ callbacks[type].push(init);
324
+
325
+ load(config, true, config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
326
+ if (err) {
327
+ status[type] = 'fail';
328
+ throwError('networkError', config);
329
+ } else {
330
+ status[type] = 'loaded';
331
+ var cbs = callbacks[type];
332
+ for (var i = 0, len = cbs.length; i < len; i = i + 1) {
333
+ var cb = cbs[i];
334
+ if (isFunction(cb)) {
335
+ cb();
336
+ }
337
+ }
338
+ callbacks[type] = [];
339
+ }
340
+ });
341
+ } else if (s === "loaded") {
342
+ init();
343
+ } else if (s === "fail") {
344
+ throwError('networkError', config);
345
+ } else if (s === "loading") {
346
+ callbacks[type].push(init);
347
+ }
348
+ });
349
+
350
+ };
351
+
352
+
353
+ })(window);