cloudflare 3.2.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -16
- data/README.md +9 -9
- data/Rakefile +10 -8
- data/cloudflare.gemspec +19 -17
- data/lib/cloudflare.rb +20 -5
- data/lib/cloudflare/{response.rb → accounts.rb} +25 -47
- data/lib/cloudflare/connection.rb +31 -52
- data/lib/cloudflare/dns.rb +91 -0
- data/lib/cloudflare/firewall.rb +80 -0
- data/lib/cloudflare/logs.rb +41 -0
- data/lib/cloudflare/paginate.rb +54 -0
- data/lib/cloudflare/representation.rb +94 -0
- data/lib/cloudflare/rspec/connection.rb +18 -11
- data/lib/cloudflare/user.rb +10 -9
- data/lib/cloudflare/version.rb +1 -1
- data/lib/cloudflare/zones.rb +85 -0
- data/spec/cloudflare/dns_spec.rb +32 -0
- data/spec/cloudflare/firewall_spec.rb +48 -0
- data/spec/cloudflare/zone_spec.rb +23 -124
- data/spec/spec_helper.rb +25 -169
- metadata +37 -19
- data/.rubocop.yml +0 -19
- data/.ruby-version +0 -1
- data/lib/cloudflare/zone.rb +0 -202
- data/spec/fake_cloudflare/cloudflare.rb +0 -17
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require_relative 'representation'
|
24
|
+
require_relative 'paginate'
|
25
|
+
|
26
|
+
module Cloudflare
|
27
|
+
module Firewall
|
28
|
+
class Rule < Representation
|
29
|
+
def mode
|
30
|
+
value[:mode]
|
31
|
+
end
|
32
|
+
|
33
|
+
def notes
|
34
|
+
value[:notes]
|
35
|
+
end
|
36
|
+
|
37
|
+
def configuration
|
38
|
+
value[:configuration]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"#{configuration[:value]} - #{mode} - #{notes}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Rules < Representation
|
47
|
+
include Paginate
|
48
|
+
|
49
|
+
def representation
|
50
|
+
Rule
|
51
|
+
end
|
52
|
+
|
53
|
+
def set(mode, value, notes: nil, target: 'ip')
|
54
|
+
notes ||= "cloudflare gem [#{mode}] #{Time.now.strftime('%m/%d/%y')}"
|
55
|
+
|
56
|
+
message = self.post({
|
57
|
+
mode: mode.to_s,
|
58
|
+
notes: notes,
|
59
|
+
configuration: {
|
60
|
+
target: target,
|
61
|
+
value: value.to_s,
|
62
|
+
}
|
63
|
+
})
|
64
|
+
|
65
|
+
id = message.result[:id]
|
66
|
+
resource = @resource.with(path: id)
|
67
|
+
|
68
|
+
return representation.new(resource, metadata: message.headers, value: message.result)
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_by_id(id)
|
72
|
+
Rule.new(@resource.with(path: id))
|
73
|
+
end
|
74
|
+
|
75
|
+
def each_by_value(value, &block)
|
76
|
+
each(configuration_value: value, &block)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'representation'
|
22
|
+
require_relative 'paginate'
|
23
|
+
|
24
|
+
module Cloudflare
|
25
|
+
module Logs
|
26
|
+
class Entry < Representation
|
27
|
+
def to_s
|
28
|
+
"#{value[:rayid]}-#{value[:ClientRequestURI]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Received < Representation
|
33
|
+
include Paginate
|
34
|
+
|
35
|
+
def representation
|
36
|
+
Entry
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
module Cloudflare
|
22
|
+
module Paginate
|
23
|
+
include Enumerable
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
self.value.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def representation
|
30
|
+
Representation
|
31
|
+
end
|
32
|
+
|
33
|
+
def each(page: 1, per_page: 50, **parameters)
|
34
|
+
return to_enum(:each, page: page, per_page: per_page, **parameters) unless block_given?
|
35
|
+
|
36
|
+
while true
|
37
|
+
zones = @resource.get(self.class, page: page, per_page: per_page, **parameters)
|
38
|
+
|
39
|
+
break if zones.empty?
|
40
|
+
|
41
|
+
Array(zones.value).each do |attributes|
|
42
|
+
resource = @resource.with(path: attributes[:id])
|
43
|
+
|
44
|
+
yield representation.new(resource, metadata: zones.metadata, value: attributes)
|
45
|
+
end
|
46
|
+
|
47
|
+
page += 1
|
48
|
+
|
49
|
+
# Was this the last page?
|
50
|
+
break if zones.value.count < per_page
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright, 2012, by Marcin Prokop.
|
4
|
+
# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
14
|
+
# all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
# THE SOFTWARE.
|
23
|
+
|
24
|
+
require 'json'
|
25
|
+
|
26
|
+
require 'async/rest/representation'
|
27
|
+
|
28
|
+
module Cloudflare
|
29
|
+
class RequestError < StandardError
|
30
|
+
def initialize(resource, errors)
|
31
|
+
super("#{resource}: #{errors.map{|attributes| attributes[:message]}.join(', ')}")
|
32
|
+
|
33
|
+
@representation = representation
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :representation
|
37
|
+
end
|
38
|
+
|
39
|
+
class Message
|
40
|
+
def initialize(response)
|
41
|
+
@response = response
|
42
|
+
@body = response.read
|
43
|
+
end
|
44
|
+
|
45
|
+
attr :response
|
46
|
+
attr :body
|
47
|
+
|
48
|
+
def headers
|
49
|
+
@response.headers
|
50
|
+
end
|
51
|
+
|
52
|
+
def result
|
53
|
+
@body[:result]
|
54
|
+
end
|
55
|
+
|
56
|
+
def read
|
57
|
+
@body[:result]
|
58
|
+
end
|
59
|
+
|
60
|
+
def results
|
61
|
+
Array(result)
|
62
|
+
end
|
63
|
+
|
64
|
+
def errors
|
65
|
+
@body[:errors]
|
66
|
+
end
|
67
|
+
|
68
|
+
def messages
|
69
|
+
@body[:messages]
|
70
|
+
end
|
71
|
+
|
72
|
+
def success?
|
73
|
+
@body[:success]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Representation < Async::REST::Representation
|
78
|
+
def process_response(*)
|
79
|
+
message = Message.new(super)
|
80
|
+
|
81
|
+
unless message.success?
|
82
|
+
raise RequestError.new(@resource, message.errors)
|
83
|
+
end
|
84
|
+
|
85
|
+
return message
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_hash
|
89
|
+
if value.is_a?(Hash)
|
90
|
+
return value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -20,19 +20,26 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
+
require 'async/rspec'
|
23
24
|
require_relative '../../cloudflare'
|
24
25
|
|
25
26
|
module Cloudflare
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
module RSpec
|
28
|
+
module Connection
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
RSpec.shared_context Connection do
|
32
|
+
include_context Async::RSpec::Reactor
|
33
|
+
|
34
|
+
# You must specify these in order for the tests to run.
|
35
|
+
let(:email) {ENV['CLOUDFLARE_EMAIL']}
|
36
|
+
let(:key) {ENV['CLOUDFLARE_KEY']}
|
37
|
+
|
38
|
+
let(:connection) {@connection = Cloudflare.connect(key: key, email: email)}
|
39
|
+
|
40
|
+
after do
|
41
|
+
@connection&.close
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
38
45
|
end
|
data/lib/cloudflare/user.rb
CHANGED
@@ -21,15 +21,16 @@
|
|
21
21
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
22
|
# THE SOFTWARE.
|
23
23
|
|
24
|
-
require_relative '
|
24
|
+
require_relative 'representation'
|
25
25
|
|
26
26
|
module Cloudflare
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
class User < Representation
|
28
|
+
def id
|
29
|
+
value[:id]
|
30
|
+
end
|
31
|
+
|
32
|
+
def email
|
33
|
+
value[:email]
|
34
|
+
end
|
35
|
+
end
|
35
36
|
end
|
data/lib/cloudflare/version.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright, 2012, by Marcin Prokop.
|
4
|
+
# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
|
+
# Copyright, 2017, by David Rosenbloom. <http://artifactory.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
require_relative 'representation'
|
26
|
+
require_relative 'paginate'
|
27
|
+
|
28
|
+
require_relative 'firewall'
|
29
|
+
require_relative 'dns'
|
30
|
+
require_relative 'logs'
|
31
|
+
|
32
|
+
module Cloudflare
|
33
|
+
class Zone < Representation
|
34
|
+
def dns_records
|
35
|
+
DNS::Records.new(@resource.with(path: 'dns_records'))
|
36
|
+
end
|
37
|
+
|
38
|
+
def firewall_rules
|
39
|
+
Firewall::Rules.new(@resource.with(path: 'firewall/access_rules/rules'))
|
40
|
+
end
|
41
|
+
|
42
|
+
def logs
|
43
|
+
Logs::Received.new(@resource.with(path: 'logs/received'))
|
44
|
+
end
|
45
|
+
|
46
|
+
DEFAULT_PURGE_CACHE_PARAMS = {
|
47
|
+
purge_everything: true
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
def purge_cache(parameters = DEFAULT_PURGE_CACHE_PARAMS)
|
51
|
+
message = self.with(path: 'purge_cache').post(parameters)
|
52
|
+
|
53
|
+
return message.success?
|
54
|
+
end
|
55
|
+
|
56
|
+
def name
|
57
|
+
value[:name]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Zones < Representation
|
62
|
+
include Paginate
|
63
|
+
|
64
|
+
def representation
|
65
|
+
Zone
|
66
|
+
end
|
67
|
+
|
68
|
+
def create(name, account, jump_start = false)
|
69
|
+
message = self.post(name: name, account: account.to_hash, jump_start: jump_start)
|
70
|
+
|
71
|
+
id = message.result[:id]
|
72
|
+
resource = @resource.with(path: id)
|
73
|
+
|
74
|
+
return representation.new(resource, metadata: message.headers, value: message.result)
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_by_name(name)
|
78
|
+
each(name: name).first
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_by_id(id)
|
82
|
+
Zone.new(@resource.with(path: id))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'cloudflare/rspec/connection'
|
3
|
+
|
4
|
+
RSpec.describe Cloudflare::DNS, order: :defined, timeout: 30 do
|
5
|
+
include_context Cloudflare::Zone
|
6
|
+
|
7
|
+
let(:subdomain) {"www#{ENV['TRAVIS_JOB_ID']}"}
|
8
|
+
|
9
|
+
let(:record) {@record = zone.dns_records.create("A", subdomain, "1.2.3.4")}
|
10
|
+
|
11
|
+
after do
|
12
|
+
if defined? @record
|
13
|
+
expect(@record.delete).to be_success
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can create dns record" do
|
18
|
+
expect(record.type).to be == "A"
|
19
|
+
expect(record.name).to be_start_with subdomain
|
20
|
+
expect(record.content).to be == "1.2.3.4"
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with existing record" do
|
24
|
+
it "can update dns content" do
|
25
|
+
record.update_content("4.3.2.1")
|
26
|
+
expect(record.content).to be == "4.3.2.1"
|
27
|
+
|
28
|
+
fetched_record = zone.dns_records.find_by_name(record.name)
|
29
|
+
expect(fetched_record.content).to be == record.content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|