cloudflare_client_rb 4.0.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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cloudflare_client.rb +211 -0
  3. data/lib/cloudflare_client/certificate.rb +40 -0
  4. data/lib/cloudflare_client/organization.rb +26 -0
  5. data/lib/cloudflare_client/organization/access_rule.rb +76 -0
  6. data/lib/cloudflare_client/organization/invite.rb +53 -0
  7. data/lib/cloudflare_client/organization/member.rb +37 -0
  8. data/lib/cloudflare_client/organization/railgun.rb +79 -0
  9. data/lib/cloudflare_client/organization/role.rb +19 -0
  10. data/lib/cloudflare_client/railgun.rb +63 -0
  11. data/lib/cloudflare_client/version.rb +3 -0
  12. data/lib/cloudflare_client/virtual_dns_cluster.rb +133 -0
  13. data/lib/cloudflare_client/virtual_dns_cluster/analytic.rb +38 -0
  14. data/lib/cloudflare_client/zone.rb +129 -0
  15. data/lib/cloudflare_client/zone/analytics.rb +56 -0
  16. data/lib/cloudflare_client/zone/base.rb +9 -0
  17. data/lib/cloudflare_client/zone/custom_hostname.rb +86 -0
  18. data/lib/cloudflare_client/zone/custom_page.rb +28 -0
  19. data/lib/cloudflare_client/zone/custom_ssl.rb +62 -0
  20. data/lib/cloudflare_client/zone/dns.rb +66 -0
  21. data/lib/cloudflare_client/zone/firewall.rb +3 -0
  22. data/lib/cloudflare_client/zone/firewall/access_rule.rb +87 -0
  23. data/lib/cloudflare_client/zone/firewall/waf_package.rb +46 -0
  24. data/lib/cloudflare_client/zone/firewall/waf_package/base.rb +9 -0
  25. data/lib/cloudflare_client/zone/firewall/waf_package/rule.rb +46 -0
  26. data/lib/cloudflare_client/zone/firewall/waf_package/rule_group.rb +42 -0
  27. data/lib/cloudflare_client/zone/keyless_ssl.rb +56 -0
  28. data/lib/cloudflare_client/zone/log.rb +51 -0
  29. data/lib/cloudflare_client/zone/page_rule.rb +64 -0
  30. data/lib/cloudflare_client/zone/railgun_connections.rb +43 -0
  31. data/lib/cloudflare_client/zone/rate_limit.rb +73 -0
  32. data/lib/cloudflare_client/zone/ssl.rb +28 -0
  33. data/lib/cloudflare_client/zone/ssl/certificate_pack.rb +32 -0
  34. data/lib/cloudflare_client/zone/subscription.rb +55 -0
  35. metadata +76 -0
@@ -0,0 +1,56 @@
1
+ class CloudflareClient::Zone::Analytics < CloudflareClient::Zone::Base
2
+ ##
3
+ # zone analytics (free, pro, business, enterprise)
4
+
5
+ ##
6
+ # return dashboard data for a given zone or colo
7
+ def zone_dashboard
8
+ cf_get(path: "/zones/#{zone_id}/analytics/dashboard")
9
+ end
10
+
11
+ ##
12
+ # creturn analytics for colos for a time window.
13
+ # since and untill must be RFC 3339 timestamps
14
+ # TODO: support continuous
15
+ def colo_dashboard(since_ts: nil, until_ts: nil)
16
+ raise 'since_ts must be a valid timestamp' if since_ts.nil? || !date_rfc3339?(since_ts)
17
+ raise 'until_ts must be a valid timestamp' if until_ts.nil? || !date_rfc3339?(until_ts)
18
+
19
+ cf_get(path: "/zones/#{zone_id}/analytics/dashboard")
20
+ end
21
+
22
+ ##
23
+ # DNS analytics
24
+
25
+ ##
26
+ # return a table of analytics
27
+ def dns_table
28
+ cf_get(path: "/zones/#{zone_id}/dns_analytics/report")
29
+ end
30
+
31
+ ##
32
+ # return analytics by time
33
+ def dns_by_time(dimensions: [],
34
+ metrics: [],
35
+ sort: [],
36
+ filters: [],
37
+ since_ts: nil,
38
+ until_ts: nil,
39
+ limit: 100,
40
+ time_delta: 'hour')
41
+ # TODO: what are valid dimensions?
42
+ # TODO: what are valid metrics?
43
+ unless since_ts.nil?
44
+ raise 'since_ts must be a valid timestamp' unless date_rfc3339?(since_ts)
45
+ end
46
+ unless until_ts.nil?
47
+ raise 'until_ts must be a valid timestamp' unless date_rfc3339?(until_ts)
48
+ end
49
+
50
+ params = {limit: limit, time_delta: time_delta}
51
+ params['since'] = since_ts
52
+ params['until'] = until_ts
53
+
54
+ cf_get(path: "/zones/#{zone_id}/dns_analytics/report/bytime", params: params)
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ class CloudflareClient::Zone::Base < CloudflareClient::Zone
2
+ attr_reader :zone_id
3
+
4
+ def initialize(args)
5
+ @zone_id = args.delete(:zone_id)
6
+ id_check('zone_id', zone_id)
7
+ super
8
+ end
9
+ end
@@ -0,0 +1,86 @@
1
+ # https://api.cloudflare.com/#custom-hostname-for-a-zone-list-custom-hostnames
2
+ class CloudflareClient::Zone::CustomHostname < CloudflareClient::Zone::Base
3
+ VALID_METHODS = %w[http email cname].freeze
4
+ VALID_TYPES = ['read only', 'dv'].freeze
5
+ VALID_ORDERS = %w[ssl ssl_status].freeze
6
+ DEFAULT_SSL_PROPERTIES = { method: 'http', type: 'dv' }.freeze
7
+
8
+ ##
9
+ # create custom_hostname
10
+ # - :custom_metadata may only work for enterprise or better customers
11
+ # - :ssl has undocumented properties: 'custom_certificate' and 'custom_key', or can be nulled
12
+ def create(hostname:, ssl: DEFAULT_SSL_PROPERTIES, custom_metadata: {})
13
+ #FIXME: implement checks for the custom_metedata/find out of it's going to be exposed to anyone else
14
+ #"custom_metadata":{"origin_override":"hostname.zendesk.com"}
15
+ #"custom_metadata":{"hsts_enabled":"true"}
16
+ #"custom_metadata":{"hsts_enabled":"true","custom_maxage":value}
17
+ id_check('hostname', hostname)
18
+
19
+ if ssl && ssl[:method] && ssl[:type]
20
+ valid_value_check(:method, ssl[:method], VALID_METHODS)
21
+ valid_value_check(:type, ssl[:type], VALID_TYPES)
22
+ end
23
+
24
+ data = { hostname: hostname, ssl: ssl }
25
+ data[:custom_metadata] = custom_metadata unless custom_metadata.empty?
26
+
27
+ cf_post(path: "/zones/#{zone_id}/custom_hostnames", data: data)
28
+ end
29
+
30
+ ##
31
+ # list custom_hostnames
32
+ def list(hostname: nil, id: nil, page: 1, per_page: 50, order: 'ssl', direction: 'desc', ssl: 0)
33
+ raise 'cannot use both hostname and id' if hostname && id
34
+ valid_value_check(:order, order, VALID_ORDERS)
35
+ valid_value_check(:direction, direction, VALID_DIRECTIONS)
36
+ valid_value_check(:ssl, ssl, [0, 1])
37
+
38
+ params = {page: page, per_page: per_page, order: order, direction: direction, ssl: ssl}
39
+ params[:hostname] = hostname if hostname
40
+ params[:id] = id if id
41
+
42
+ cf_get(path: "/zones/#{zone_id}/custom_hostnames", params: params)
43
+ end
44
+
45
+ ##
46
+ # details of a custom hostname
47
+ def show(id:)
48
+ id_check('id', id)
49
+
50
+ cf_get(path: "/zones/#{zone_id}/custom_hostnames/#{id}")
51
+ end
52
+
53
+ ##
54
+ # update a custom hosntame
55
+ # https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
56
+ def update(id:, ssl: {}, custom_metadata: nil)
57
+ id_check('id', id)
58
+
59
+ data = {}
60
+
61
+ if ssl && ssl[:method] && ssl[:type]
62
+ valid_value_check(:method, ssl[:method], VALID_METHODS)
63
+ valid_value_check(:type, ssl[:type], VALID_TYPES)
64
+ end
65
+
66
+ # Setting this to "null" requests removal of the attached certificate. We're
67
+ # using {} as the default value to denote "don't alter the SSL".
68
+ data[:ssl] = ssl unless ssl == {}
69
+
70
+ unless custom_metadata.nil?
71
+ raise 'custom_metadata must be an object' unless custom_metadata.is_a?(Hash)
72
+
73
+ data[:custom_metadata] = custom_metadata
74
+ end
75
+
76
+ cf_patch(path: "/zones/#{zone_id}/custom_hostnames/#{id}", data: data)
77
+ end
78
+
79
+ ##
80
+ # delete a custom hostname and ssl certs
81
+ def delete(id:)
82
+ id_check('id', id)
83
+
84
+ cf_delete(path: "/zones/#{zone_id}/custom_hostnames/#{id}")
85
+ end
86
+ end
@@ -0,0 +1,28 @@
1
+ class CloudflareClient::Zone::CustomPage < CloudflareClient::Zone::Base
2
+ ##
3
+ # Custom pages for a zone
4
+ ##
5
+ # custom_pages list all avaialble custom_pages
6
+ def list
7
+ cf_get(path: "/zones/#{zone_id}/custom_pages")
8
+ end
9
+
10
+ ##
11
+ # custom_page details
12
+ def show(id:)
13
+ raise 'id must not be nil' if id.nil?
14
+ cf_get(path: "/zones/#{zone_id}/custom_pages/#{id}")
15
+ end
16
+
17
+ ##
18
+ # update_custom_page
19
+ def update(id:, url:, state:)
20
+ id_check('id', id)
21
+ id_check('url', url)
22
+ raise 'state must be either default | customized' unless %w[default customized].include?(state)
23
+
24
+ data = {url: url, state: state}
25
+
26
+ cf_put(path: "/zones/#{zone_id}/custom_pages/#{id}", data: data)
27
+ end
28
+ end
@@ -0,0 +1,62 @@
1
+ class CloudflareClient::Zone::CustomSSL < CloudflareClient::Zone::Base
2
+ VALID_ORDERS = %w[status issuer priority expires_on].freeze
3
+
4
+ ##
5
+ # Custom SSL for a zone
6
+
7
+ ##
8
+ # create custom ssl for a zone
9
+ def create(certificate:, private_key:, bundle_method: nil)
10
+ id_check('certificate', certificate)
11
+ id_check('private_key', private_key)
12
+ bundle_method_check(bundle_method) unless bundle_method.nil?
13
+ # TODO: validate the cert/key using openssl? Could be difficult if they are
14
+ # privately generated
15
+ data = {certificate: certificate, private_key: private_key}
16
+ data[:bundle_method] = bundle_method unless bundle_method.nil?
17
+ cf_post(path: "/zones/#{zone_id}/custom_certificates", data: data)
18
+ end
19
+
20
+ ##
21
+ # list custom ssl configurations
22
+ def list(page: 1, per_page: 50, order: 'priority', direction: 'asc', match: 'all')
23
+ raise ("order must be one of #{VALID_ORDERS}") unless VALID_ORDERS.include?(order)
24
+ raise ('direction must be asc || desc') unless (direction == 'asc' || direction == 'desc')
25
+ raise ('match must be all || any') unless (match == 'any' || match == 'all')
26
+ params = {page: page, per_page: per_page}
27
+ params[:match] = match
28
+ params[:direction] = direction
29
+ cf_get(path: "/zones/#{zone_id}/custom_certficates", params: params)
30
+ end
31
+
32
+ ##
33
+ # details of a single config
34
+ def show(configuration_id:)
35
+ raise 'ssl configuration id required' if configuration_id.nil?
36
+ cf_get(path: "/zones/#{zone_id}/custom_certificates/#{configuration_id}")
37
+ end
38
+
39
+ ##
40
+ # updates a custom ssl record
41
+ def update(id:, private_key: nil, certificate: nil, bundle_method: nil)
42
+ id_check('id', id)
43
+ id_check('private_key must be provided') if private_key.nil?
44
+ bundle_method_check(bundle_method)
45
+ data = {private_key: private_key, certificate: certificate, bundle_method: bundle_method}
46
+ cf_patch(path: "/zones/#{zone_id}/custom_certificates/#{id}", data: data)
47
+ end
48
+
49
+ ##
50
+ # re-prioritize ssl certs data = [{id: "cert_id", priority: 2}, {id: "cert_id", priority: 1}]
51
+ def prioritize(data: [])
52
+ raise 'must provide an array of certifiates and priorities' unless data.is_a?(Array) && !data.empty?
53
+ cf_put(path: "/zones/#{zone_id}/custom_certificates/prioritize", data: data)
54
+ end
55
+
56
+ ##
57
+ # delete a custom ssl cert
58
+ def delete(id:)
59
+ id_check('id', id)
60
+ cf_delete(path: "/zones/#{zone_id}/custom_certificates/#{id}")
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ class CloudflareClient::Zone::DNS < CloudflareClient::Zone::Base
2
+ VALID_TYPES = ['A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'LOC', 'MX', 'NS', 'SPF', 'read only'].freeze
3
+
4
+ ##
5
+ # DNS methods
6
+
7
+ ##
8
+ # Create a dns record
9
+ def create(name:, type:, content:, ttl: nil, proxied: nil)
10
+ raise ("type must be one of #{VALID_TYPES.flatten}") unless VALID_TYPES.include?(type)
11
+ data = {name: name, type: type, content: content}
12
+ cf_post(path: "/zones/#{zone_id}/dns_records", data: data)
13
+ end
14
+
15
+ ##
16
+ # list/search for dns records in a given zone
17
+ def list(name: nil, content: nil, per_page: 50, page_no: 1, order: 'type', match: 'all', type: nil)
18
+ raise('match must be either all | any') unless %w[all any].include?(match)
19
+ params = {per_page: per_page, page: page_no, order: order}
20
+ params[:name] = name unless name.nil?
21
+ params[:content] = content unless content.nil?
22
+ params[:type] = type unless type.nil?
23
+ cf_get(path: "/zones/#{zone_id}/dns_records", params: params)
24
+ end
25
+
26
+ ##
27
+ # details for a given dns_record
28
+ def show(id:)
29
+ id_check('dns record id', id)
30
+ cf_get(path: "/zones/#{zone_id}/dns_records/#{id}")
31
+ end
32
+
33
+ ##
34
+ # update a dns record.
35
+ # zone_id, id, type, and name are all required. ttl and proxied are optional
36
+ def update(id:, type:, name:, content:, ttl: nil, proxied: nil)
37
+ id_check('dns record id', id)
38
+ id_check('dns record type', type)
39
+ id_check('dns record name', name)
40
+ id_check('dns record content', content)
41
+ raise('must suply type, name, and content') if (type.nil? || name.nil? || content.nil?)
42
+ data = {type: type, name: name, content: content}
43
+ data[:ttl] = ttl unless ttl.nil?
44
+ data[:proxied] = proxied unless proxied.nil?
45
+ cf_put(path: "/zones/#{zone_id}/dns_records/#{id}", data: data)
46
+ end
47
+
48
+ ##
49
+ # delete a dns record
50
+ # id is required. ttl and proxied are optional
51
+ def delete(id:)
52
+ id_check('id', id)
53
+ cf_delete(path: "/zones/#{zone_id}/dns_records/#{id}")
54
+ end
55
+
56
+ ##
57
+ # import a BIND formatted zone file
58
+ # def import_zone_file(path_to_file: nil)
59
+ # # FIXME: not tested
60
+ # raise("full path of file to import") if path_to_file.nil?
61
+ # # TODO: ensure that this is a bind file?
62
+ # raise("import file_name does not exist") if File.exists?(path_to_file)
63
+ # data = { file: Faraday::UploadIO.new(path_to_file, 'multipart/form-data') }
64
+ # cf_post(path: "/v4/zones/#{zone_id}/dns_records/import", data: data)
65
+ # end
66
+ end
@@ -0,0 +1,3 @@
1
+ class CloudflareClient::Zone::Firewall < CloudflareClient::Zone::Base
2
+ Dir[File.expand_path('../firewall/*.rb', __FILE__)].each {|f| require f}
3
+ end
@@ -0,0 +1,87 @@
1
+ class CloudflareClient::Zone::Firewall::AccessRule < CloudflareClient::Zone::Firewall
2
+ VALID_MODES = %w[block challenge whitelist].freeze
3
+ VALID_SCOPE_TYPES = %w[user organization zone].freeze
4
+ VALID_CONFIG_TARGETS = %w[ip ip_range country].freeze
5
+ VALID_ORDERS = %w[scope_type configuration_target configuration_value mode].freeze
6
+ VALID_CASCADES = %w[none basic aggressive].freeze
7
+
8
+ ##
9
+ # firewall_access_rules_for_a_zone
10
+ def list(notes: nil, mode: nil, match: nil, scope_type: nil, configuration_value: nil, order: nil, page: 1, per_page: 50, configuration_target: nil, direction: 'desc')
11
+ params = {page: page, per_page: per_page}
12
+ params[:notes] = notes unless notes.nil?
13
+ params[:configuration_value] = configuration_value unless configuration_value.nil?
14
+
15
+ unless mode.nil?
16
+ valid_value_check(:mode, mode, VALID_MODES)
17
+ params[:mode] = mode
18
+ end
19
+
20
+ unless match.nil?
21
+ valid_value_check(:match, match, VALID_MATCHES)
22
+ params[:match] = match
23
+ end
24
+
25
+ unless scope_type.nil?
26
+ valid_value_check(:scope_type, scope_type, VALID_SCOPE_TYPES)
27
+ params[:scope_type] = scope_type
28
+ end
29
+
30
+ unless configuration_target.nil?
31
+ valid_value_check(:configuration_target, configuration_target, VALID_CONFIG_TARGETS)
32
+ params[:configuration_target] = configuration_target
33
+ end
34
+
35
+ unless direction.nil?
36
+ valid_value_check(:direction, direction, VALID_DIRECTIONS)
37
+ params[:direction] = direction
38
+ end
39
+
40
+ unless order.nil?
41
+ valid_value_check(:order, order, VALID_ORDERS)
42
+ params[:order] = order
43
+ end
44
+
45
+ cf_get(path: "/zones/#{zone_id}/firewall/access_rules/rules", params: params)
46
+ end
47
+
48
+ ##
49
+ # create firewall access rule
50
+ def create(mode:, configuration:, notes: nil)
51
+ valid_value_check(:mode, mode, VALID_MODES)
52
+ if configuration.is_a?(Hash)
53
+ unless configuration.keys.map(&:to_sym).sort == [:target, :value]
54
+ raise 'configuration must contain valid a valid target and value'
55
+ end
56
+ else
57
+ raise 'configuration must be a valid configuration object'
58
+ end
59
+
60
+ data = {mode: mode, configuration: configuration}
61
+ data[:notes] = notes unless notes.nil?
62
+
63
+ cf_post(path: "/zones/#{zone_id}/firewall/access_rules/rules", data: data)
64
+ end
65
+
66
+ ##
67
+ # updates firewall_access_rule
68
+ def update(id:, mode: nil, notes: nil)
69
+ id_check('id', id)
70
+ valid_value_check(:mode, mode, VALID_MODES) unless mode.nil?
71
+
72
+ data = {}
73
+ data[:mode] = mode unless mode.nil?
74
+ data[:notes] = notes unless notes.nil?
75
+
76
+ cf_patch(path: "/zones/#{zone_id}/firewall/access_rules/rules/#{id}", data: data)
77
+ end
78
+
79
+ ##
80
+ # delete a firewall access rule
81
+ def delete(id:, cascade: 'none')
82
+ id_check('id', id)
83
+ valid_value_check(:cascade, cascade, VALID_CASCADES)
84
+
85
+ cf_delete(path: "/zones/#{zone_id}/firewall/access_rules/rules/#{id}")
86
+ end
87
+ end
@@ -0,0 +1,46 @@
1
+ class CloudflareClient::Zone::Firewall::WAFPackage < CloudflareClient::Zone::Firewall
2
+ require_relative './waf_package/base.rb'
3
+ Dir[File.expand_path('../waf_package/*.rb', __FILE__)].each {|f| require f}
4
+
5
+ VALID_ORDERS = %w[status name].freeze
6
+ VALID_SENSITIVITIES = %w[high low off].freeze
7
+ VALID_ACTION_MODES = %w[simulate block challenge].freeze
8
+
9
+ ##
10
+ # lists waf_rule_packages
11
+ def list(name: nil, page: 1, per_page: 50, order: 'status', direction: 'desc', match: 'all')
12
+ params = {page: page, per_page: per_page}
13
+ params[:name] = name unless name.nil?
14
+
15
+ valid_value_check(:order, order, VALID_ORDERS)
16
+ params[:order] = order
17
+
18
+ valid_value_check(:direction, direction, VALID_DIRECTIONS)
19
+ params[:direction] = direction
20
+
21
+ valid_value_check(:match, match, VALID_MATCHES)
22
+ params[:match] = match
23
+
24
+ cf_get(path: "/zones/#{zone_id}/firewall/waf/packages", params: params)
25
+ end
26
+
27
+ ##
28
+ # shows details of a single package
29
+ def show(id:)
30
+ id_check('id', id)
31
+
32
+ cf_get(path: "/zones/#{zone_id}/firewall/waf/packages/#{id}")
33
+ end
34
+
35
+ ##
36
+ # changes the sensitivity and action for an anomaly detection type WAF rule package
37
+ def update(id:, sensitivity: 'high', action_mode: 'challange')
38
+ id_check('id', id)
39
+ valid_value_check(:sensitivity, sensitivity, VALID_SENSITIVITIES)
40
+ valid_value_check(:action_mode, action_mode, VALID_ACTION_MODES)
41
+
42
+ data = {sensitivity: sensitivity, action_mode: action_mode}
43
+
44
+ cf_patch(path: "/zones/#{zone_id}/firewall/waf/packages/#{id}", data: data)
45
+ end
46
+ end