dnsmadeeasy 0.2.3 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f3fe4a8e5e7490cd827fe1c39b7cb9eec1458ee6
4
- data.tar.gz: 6b3f939c99808e319bcd77a4f77f34a456a3957f
3
+ metadata.gz: 0f567aee61ac65f6e851a7499a66f0da8639d1d9
4
+ data.tar.gz: 44825d6fcde3259791f194696da581ebbebc658c
5
5
  SHA512:
6
- metadata.gz: ceed4e0e1ac07e7047ba55b39e723c6203c050add20167419f200ef2be3c513400b6a9b628c496bf7ab5e6177cd52be072213fcaa7edecf231bd0c19772b8613
7
- data.tar.gz: 5d33110950c9d523d9c7a25bd4f57f3cef57f42a788d8f138374d67dfb1bbf991850f3133b0e994c79c6c08a7ec9bd136b2b48736193a1a08b37550cba80abb9
6
+ metadata.gz: 3935c9a7b0211ca35321ce1040352e12d8df876d8f1cc4b049bd987a711596cc353c5caa57a47b2dd97b3d4d7569f58a5da4e47b2f83351527c0340db94a9545
7
+ data.tar.gz: 70d6e6a69d8e8f66e5875d45dba02ac6ac50da4092ba4e1da195ac525d35e894fa01cf439f54576e1883003c2f1ee9877bb96f1b3277ece96b692b3fe5cd8246
@@ -0,0 +1,30 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: false
5
+ config:
6
+ languages:
7
+ - ruby
8
+ - javascript
9
+ - python
10
+ - php
11
+ fixme:
12
+ enabled: false
13
+ rubocop:
14
+ enabled: true
15
+ checks:
16
+ Rubocop/Metrics/MethodLength:
17
+ enabled: false
18
+ Rubocop/Metrics/CyclomaticComplexity:
19
+ enabled: false
20
+ ratings:
21
+ paths:
22
+ - "**.inc"
23
+ - "**.js"
24
+ - "**.jsx"
25
+ - "**.module"
26
+ - "**.php"
27
+ - "**.py"
28
+ - "**.rb"
29
+ exclude_paths:
30
+ - spec/
data/README.md CHANGED
@@ -12,36 +12,20 @@ This is a fully featured REST API client for DnsMadeEasy provider. DME is an **e
12
12
 
13
13
  **DnsMadeEasy** allows you to fetch, create, update DNS records, as long as you know your API key and the secret.
14
14
 
15
+ ### Setting up Credentials
16
+
15
17
  You can find your API Key and Secret on the [Account Settings Page](https://cp.dnsmadeeasy.com/account/info) of their UI.
16
18
 
17
19
  Once you have the key and the secret, you have several choices:
18
20
 
19
- 1. Perhaps the most conveniently, you can store them in a small YAML file, that must be placed in a specific location within your home folder: `~/.dnsmadeeasy/credentials.yml`. The file should look like this one below (NOTE: these are not real credentials, btw):
20
-
21
- ```yaml
22
- # file: ~/.dnsmadeeasy/credentials.yml
23
- credentials:
24
- api_key: 2062259f-f666b17-b1fa3b48-042ad4030
25
- api_secret: 2265bc3-e31ead-95b286312e-c215b6a0
26
- ```
27
-
28
- With this file existing, you can query right away, by using the shortcut module `DME`, such as
29
-
30
- ```ruby
31
- require 'dme'
32
- DME.domains.data.first.name #=> 'moo.gamespot.com'
33
- ```
34
-
35
- 2. Or, you can directly instantiate a new instance of the `Client` class, by passing your API key and API secrets as arguments:
21
+ 1. You can directly instantiate a new instance of the `Client` class, by passing your API key and API secrets as arguments:
36
22
 
37
23
  ```ruby
38
24
  require 'dnsmadeeasy'
39
25
  @client = DnsMadeEasy::Api::Client.new(api_key, api_secret)
40
26
  ```
41
27
 
42
- The advantage of this method is that you can query multiple DnsMadeEasy accounts from the same Ruby VM. With other methods, only one account can be connected to.
43
-
44
- 3. Or, you can use the `DnsMadeEasy.configure` method to configure the key/secret pair, and then use `DnsMadeEasy` namespace to call the methods:
28
+ 2. Or, you can use the `DnsMadeEasy.configure` method to configure the key/secret pair, and then use `DnsMadeEasy` namespace to call the methods:
45
29
 
46
30
  ```ruby
47
31
  require 'dnsmadeeasy'
@@ -54,6 +38,63 @@ Once you have the key and the secret, you have several choices:
54
38
  DnsMadeEasy.domains.data.first.name #=> 'moo.gamespot.com'
55
39
  ```
56
40
 
41
+ Once you configure the keys, you can also use the shortcut module to save you some typing:
42
+
43
+ ```ruby
44
+ require 'dnsmadeeasy/dme'
45
+ DME.domains.data.first.name #=> 'moo.gamespot.com'
46
+ ```
47
+
48
+ This has the advantage of being much shorter, but might conflict with existing modules in your Ruby VM.
49
+ In this case, just do not require `dnsmadeeasy/dme` and only require `dnsmadeeasy`, and you'll be fine.
50
+ Otherwise, using `DME` is identical to using `DnsMadeEasy`, assuming you required `dnsmadeeasy/dme` file.
51
+
52
+ 3. Configuring API keys as above is easy, and can be done using environment variables. Alternatively, it may be convenient to store credentials in a YAML file.
53
+
54
+ * If filename is not specified, there is default location where this file is searched, which is `~/.dnsmadeeasy/credentials.yml`.
55
+ * If filename is provided, it will be read, and must conform to the following format:
56
+
57
+ **Simple Credentials Format**
58
+
59
+ ```yaml
60
+ # file: ~/.dnsmadeeasy/credentials.yml
61
+ credentials:
62
+ api_key: 2062259f-f666b17-b1fa3b48-042ad4030
63
+ api_secret: 2265bc3-e31ead-95b286312e-c215b6a0
64
+ ```
65
+
66
+ **Multi-Account Credentials Format**
67
+
68
+ Below you see two accounts, with production key and secret being encrypted. See [further below](#encryption) about encrypting your key and secrets.
69
+
70
+ ```yaml
71
+ accounts:
72
+ - name: development
73
+ default_account: true
74
+ credentials:
75
+ api_key: 12345678-a8f8-4466-ffff-2324aaaa9098
76
+ api_secret: 43009899-abcc-ffcc-eeee-09f809808098
77
+ - name: production
78
+ credentials:
79
+ api_key: "BAhTOh1TeW06OkRhdGE6OldyYXBwZXJT............"
80
+ api_secret: "BAhTOh1TeW06OkRhdGE6OldyYXBwZ............"
81
+ encryption_key: spec/fixtures/sym.key
82
+ ```
83
+
84
+ You can use the following method to access both simple and multi-account YAML configurations:
85
+
86
+ ```ruby
87
+ require 'dnsmadeeasy'
88
+ DnsMadeEasy.configure_from_file(file, account_name = nil, encryption_key = nil)
89
+
90
+ # for example:
91
+ DnsMadeEasy.configure_from_file('config/dme.yaml', 'production')
92
+ DnsMadeEasy.domains #=> [ ... ]
93
+
94
+ # or with encrypted key passed as an argument to decrypt YAML values:
95
+ DnsMadeEasy.configure_from_file('config/dme.yaml', 'production', ENV['PRODUCTION_KEY'])
96
+ ```
97
+
57
98
  ### Which Namespace to Use? What is `DME` versus `DnsMadeEasy`?
58
99
 
59
100
  Since `DnsMadeEasy` is a bit of a mouthful, we decided to offer (in addition to the standard `DnsMadeEasy` namespace) the abbreviated module `DME` that simply forwards all messages to the module `DnsMadeEasy`. If in your Ruby VM there is no conflicting top-level class `DME`, then you can `require 'dnsmadeeasy/dme'` to get all of the DnsMadeEasy client library functionality without having to type the full name once. You can even do `require 'dme'`.
@@ -67,20 +108,21 @@ In a nutshell you have three ways to access all methods provided by the [`DnsMad
67
108
  1. Instantiate and use the client class directly,
68
109
  2. Use the top-level module `DnsMadeEasy` with `require 'dnsmadeeasy'`
69
110
  3. Use the shortened top-level module `DME` with `require 'dnsmadeeasy/dme'`
70
-
111
+
112
+
71
113
  ### Examples
72
114
 
73
- If you are planning on accessing *only one DnsMadeEasy account from the same Ruby VM*, it's recommended that you save your credentials (the API key and the secret) in the above mentioned file `~/.dnsmadeeasy/credentials.yml`.
74
-
115
+ Whether or not you are accessing a single account or multiple, it is recommended that you save your credentials (the API key and the secret) encrypted in the above mentioned file `~/.dnsmadeeasy/credentials.yml` (or any file of you preference).
75
116
  ___
76
117
 
77
118
  > **NOTE:**
78
119
  >
79
- > * DO NOT check that file into your repo!
80
- > * The examples that follow assume credentials have been read from that file.
81
-
120
+ > * DO NOT check that file into your repo!
121
+ > * If you use encryption, do not check in your key!
82
122
  ___
83
123
 
124
+ The examples that follow assume credentials have already been configured, and so we explore the API.
125
+
84
126
  Using the `DME` module (or `DnsMadeEasy` if you prefer) you can access all of your records through the available API method calls, for example:
85
127
 
86
128
  ```ruby
@@ -190,6 +232,42 @@ Here is the complete of all methods supported by the `DnsMadeEasy::Api::Client`:
190
232
  * `find_first`
191
233
  * `find_record_ids`
192
234
 
235
+
236
+ <a name="encryption"></a>
237
+
238
+ ### Encryption
239
+
240
+ It was mentioned above that the credentials YAML file may contain encrypted values. This facility is provided by the encryption gem [Sym](https://github.com/kigster/sym).
241
+
242
+ In order to encrypt your values, you need to perform the following steps:
243
+
244
+ ```bash
245
+ gem install sym
246
+
247
+ # let's generate a new key and save it to a file:
248
+ sym -g -o my.key
249
+
250
+ # if you are on Mac OS-X, you can import the key into the KeyChain.
251
+ # this creates an entry in the keychain named 'my.key' that can be used later.
252
+ sym -g -x my.key
253
+ ```
254
+
255
+ Once you have the key generated, first, **make sure to never commit this to any repo!**. You can use 1Password for it, or something like that.
256
+
257
+ Let's encrypt our actual API key:
258
+
259
+ ```bash
260
+ api_key="12345678-a8f8-4466-ffff-2324aaaa9098"
261
+ api_secret="43009899-abcc-ffcc-eeee-09f809808098"
262
+ sym -ck my.key -e -s "${api_key}"
263
+ # => prints the encrypted value
264
+
265
+ # On a mac, you can copy it to clipboard:
266
+ sym -ck my.key -e -s "${api_secret}" | pbcopy
267
+ ```
268
+
269
+ Now, you place the encrypted values in the YAML file, and you can save "my.key" as the value against `encryption_key:` at the same level as the `api_key` and `api_secret` in the YAML file. This value can either point to a file path, or be a keychain name, or even a name of an environment variable. For full details, please see [sym documentation](https://github.com/kigster/sym#using-sym-with-the-command-line).
270
+
193
271
  ## CLI Client
194
272
 
195
273
  This library offers a simple CLI client `dme` that maps the command line arguments to method arguments for corresponding actions:
data/Rakefile CHANGED
@@ -24,3 +24,11 @@ end
24
24
  RSpec::Core::RakeTask.new(:spec)
25
25
 
26
26
  task :default => :spec
27
+ task :gem => :install do
28
+ gem = `ls -1 *.gemspec | sed 's/\.gemspec//g'`.chomp
29
+ puts "gem is #{gem}"
30
+ puts `gem uninstall -ax #{gem}`.chomp
31
+ file=`ls -1 pkg`.chomp
32
+ puts `gem install pkg/#{file}`
33
+ end
34
+
@@ -43,6 +43,7 @@ https://github.com/kigster/dnsmadeeasy
43
43
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
44
44
  spec.require_paths = ['lib']
45
45
 
46
+ spec.add_dependency 'sym'
46
47
  spec.add_dependency 'hashie'
47
48
  spec.add_dependency 'colored2'
48
49
  spec.add_dependency 'awesome_print'
@@ -7,36 +7,72 @@ require 'dnsmadeeasy/credentials'
7
7
  require 'dnsmadeeasy/api/client'
8
8
 
9
9
  module DnsMadeEasy
10
- class Error < StandardError; end
11
- class AuthenticationError < Error; end
12
- class APIKeyAndSecretMissingError < Error; end
10
+ class Error < StandardError;
11
+ end
12
+ class AuthenticationError < Error;
13
+ end
14
+ class APIKeyAndSecretMissingError < Error;
15
+ end
16
+ class InvalidCredentialKeys < Error;
17
+ end
18
+ class AbstractMethodError < Error;
19
+ end
20
+ class InvalidCredentialsFormatError < Error;
21
+ end
22
+ class NoSuchAccountError < Error;
23
+ end
13
24
 
14
25
  class << self
15
- attr_accessor :api_key, :api_secret
26
+ attr_accessor :default_api_key,
27
+ :default_api_secret
28
+
29
+ def configure
30
+ yield(self) if block_given?
31
+ end
32
+
33
+ def configure_from_file(file = nil,
34
+ account_name = nil,
35
+ encryption_key = nil)
16
36
 
17
- def credentials=(file)
18
- @creds = ::DnsMadeEasy::Credentials.load(file)
19
- if @creds && @creds.valid?
37
+ credentials = ::DnsMadeEasy::Credentials.keys_from_file(
38
+ filename: file || ::DnsMadeEasy::Credentials.default_credentials_path(user: ENV['USER']),
39
+ account_name: account_name,
40
+ encryption_key: encryption_key)
41
+ if credentials
20
42
  configure do |config|
21
- config.api_key = @creds.api_key
22
- config.api_secret = @creds.api_secret
43
+ config.api_key = credentials.api_key
44
+ config.api_secret = credentials.api_secret
23
45
  end
46
+ else
47
+ raise APIKeyAndSecretMissingError, "Unable to load valid api keys from #{file}!"
24
48
  end
25
49
  end
26
50
 
27
- def configure
28
- yield(self) if block_given?
51
+ def api_key=(value)
52
+ self.default_api_key = value
53
+ end
54
+
55
+ def api_secret=(value)
56
+ self.default_api_secret = value
29
57
  end
30
58
 
31
59
  def client(**options)
32
60
  @client ||= create_client(false, **options)
33
61
  end
34
62
 
35
-
36
63
  def sandbox_client(**options)
37
64
  @sandbox_client ||= create_client(true, **options)
38
65
  end
39
66
 
67
+ def create_client(sandbox = false,
68
+ api_key: self.default_api_key,
69
+ api_secret: self.default_api_secret,
70
+
71
+ **options)
72
+ raise APIKeyAndSecretMissingError, 'Please set #api_key and #api_secret' unless api_key && api_secret
73
+ ::DnsMadeEasy::Api::Client.new(api_key, api_secret, sandbox, **options)
74
+ end
75
+
40
76
  # Basically delegate it all to the Client instance
41
77
  # if the method call is supported.
42
78
  #
@@ -47,25 +83,5 @@ module DnsMadeEasy
47
83
  super(method, *args, &block)
48
84
  end
49
85
  end
50
-
51
- def default!
52
- assign_default_credentials
53
- end
54
-
55
-
56
- def assign_default_credentials
57
- if Credentials.exist?
58
- self.credentials = Credentials.default_credentials_file
59
- end
60
- end
61
-
62
- private
63
-
64
- def create_client(sandbox = false, **options)
65
- default! unless api_key && api_secret
66
- raise APIKeyAndSecretMissingError, 'Please set #api_key and #api_secret' unless api_key && api_secret
67
- ::DnsMadeEasy::Api::Client.new(api_key, api_secret, sandbox, **options)
68
- end
69
86
  end
70
-
71
87
  end
@@ -1,136 +1,98 @@
1
1
  require 'yaml'
2
+ require 'dnsmadeeasy'
3
+ require 'hashie/extensions/mash/symbolize_keys'
4
+ require 'sym'
5
+
6
+ require_relative 'credentials/api_keys'
7
+ require_relative 'credentials/yaml_file'
2
8
 
3
9
  module DnsMadeEasy
4
- # Credentials file should look like this:
10
+ # A Facade module
11
+ #
12
+ # ## Usage
13
+ #
14
+ # @creds = DnsMadeEasy::Credentials.create(key, secret)
15
+ # @creds.api_key #=> ...
16
+ # @creds.api_secret # > ...
17
+ #
18
+ # ### From a single-level YAML file that looks like this:
19
+ #
20
+ # ```yaml
21
+ # credentials:
22
+ # api_key: 12345678-a8f8-4466-ffff-2324aaaa9098
23
+ # api_secret: 43009899-abcc-ffcc-eeee-09f809808098
24
+ # ````
25
+ #
26
+ # @creds = DnsMadeEasy::Credentials.keys_from_file(filename: file)
27
+ # @creds.api_key #=> '12345678-a8f8-4466-ffff-2324aaaa9098'
5
28
  #
6
- # Usage:
29
+ # #### From a default filename ~/.dnsmadeeasy/credentials.yml
7
30
  #
8
- # Example 1. Assuming file ~/.dnsmadeeasy/credentials.yml exists:
31
+ # @creds = DnsMadeEasy::Credentials.keys_from_file
9
32
  #
10
- # DnsMadeEasy::Credentials.exist? #=> true
11
- # creds = DnsMadeEasy::Credentials.load
33
+ # ### From a multi-account file
12
34
  #
13
- # creds.api_key #=> key
14
- # creds.api_secret #=> secret
35
+ # Multi-account YAML file must look like this:
15
36
  #
37
+ # ```yaml
38
+ # accounts:
39
+ # - name: production
40
+ # default_account: true
41
+ # credentials:
42
+ # api_key: "BAhTOh1TeW06OkRhdGE6OldyYXBwZXJTdHJ1Y3QLOhNlbmNyeXB0ZWRfZGF0YSJV9HFDvF4KUwQLqevf4zvsKO1Yk04kRimAHAfNgoFO0dtRb6OjREyI43uzFV7z63FGjzXcBBG9KDUdj6OowbDw2z86nkTpakkKuIP31HCPZkQ6B2l2IhV2LPWTPSfDruDxi_ToEfbQOhBjaXBoZXJfbmFtZSIQQUVTLTI1Ni1DQkM6CXNhbHQwOgx2ZXJzaW9uaQY6DWNvbXByZXNzVA=="
43
+ # api_secret: "BAhTOh1TeW06OkRhdGE6OldyYXBwZXJTdHJ1Y3QLOhNlbmNyeXB0ZWRfZGF0YSJVHE1D3mpTsUseEdm3NWox7xdeQExobVx3-dHnEJoK9XYXawoPvtgroxOhsaYxZtxz_ZeHtSDZwu0eyDVyZ-XDo-vxalo9cQ2FOm05hVQaebo6B2l2IhVosiRfW5FnRK4BxfwPytLcOhBjaXBoZXJfbmFtZSIQQUVTLTI1Ni1DQkM6CXNhbHQwOgx2ZXJzaW9uaQY6DWNvbXByZXNzVA=="
44
+ # encryption_key: spec/fixtures/sym.key
45
+ # - name: preview
46
+ # credentials:
47
+ # api_key: "BAhTOh1TeW06OkRhdGE6OldyYXBwZXJTdHJ1Y3QLOhNlbmNyeXB0ZWRfZGF0YSJV9HFDvF4KUwQLqevf4zvsKO1Yk04kRimAHAfNgoFO0dtRb6OjREyI43uzFV7z63FGjzXcBBG9KDUdj6OowbDw2z86nkTpakkKuIP31HCPZkQ6B2l2IhV2LPWTPSfDruDxi_ToEfbQOhBjaXBoZXJfbmFtZSIQQUVTLTI1Ni1DQkM6CXNhbHQwOgx2ZXJzaW9uaQY6DWNvbXByZXNzVA=="
48
+ # api_secret: "BAhTOh1TeW06OkRhdGE6OldyYXBwZXJTdHJ1Y3QLOhNlbmNyeXB0ZWRfZGF0YSJVHE1D3mpTsUseEdm3NWox7xdeQExobVx3-dHnEJoK9XYXawoPvtgroxOhsaYxZtxz_ZeHtSDZwu0eyDVyZ-XDo-vxalo9cQ2FOm05hVQaebo6B2l2IhVosiRfW5FnRK4BxfwPytLcOhBjaXBoZXJfbmFtZSIQQUVTLTI1Ni1DQkM6CXNhbHQwOgx2ZXJzaW9uaQY6DWNvbXByZXNzVA=="
49
+ # encryption_key:
50
+ # - name: staging
51
+ # credentials:
52
+ # api_key: 12345678-a8f8-4466-ffff-2324aaaa9098
53
+ # api_secret: 43009899-abcc-ffcc-eeee-09f809808098
16
54
  #
17
- # Example 2. Assuming another file: ~/.private/dnsmadeeasy.yml:
55
+ # ```
18
56
  #
19
- # DnsMadeEasy::Credentials.exist? #=> false
20
- # DnsMadeEasy::Credentials.exist?('~/.private/dnsmadeeasy.yml') #=> true
57
+ # Here we have multiple credentials account, one of which can have 'default_account: true'
58
+ # Each account has a name that's used in `account_name` argument. Finally, if the keys
59
+ # are encrypted, the key can either be referenced in the YAML file itself (in the above
60
+ # case it points to a file name — see documentation on the gem Sym about various formats
61
+ # of the key).
21
62
  #
22
- # creds = DnsMadeEasy::Credentials.load('~/.private/dnsmadeeasy.yml')
23
- # creds.api_key #=> key
24
- # creds.api_secret #=> secret
63
+ # Note that in this case, encryption key is optional, since the YAML file
64
+ # actually specifies the key.
25
65
  #
66
+ # @creds = DnsMadeEasy::Credentials.keys_from_file(
67
+ # filename: 'spec/fixtures/credentials-multi-account.yml',
68
+ # account_name: 'production')
26
69
  #
27
- class Credentials < Hash
28
- #DEFAULT_CREDENTIALS_FILE = File.expand_path('~/.dnsmadeeasy/credentials.yml').freeze
70
+ #
71
+ # )
29
72
 
30
- class CredentialsFileNotFound < StandardError
31
- end
73
+ module Credentials
32
74
 
33
- #
34
- # Class Methods
35
- #
36
- #
37
75
  class << self
38
- # Default credential file that's used if no argument is passed.
39
- attr_accessor :default_credentials_file
40
76
 
41
- def exist?(file = default_credentials_file)
42
- File.exist?(file)
77
+ # Create a new instance of Credentials::ApiKeys
78
+ def create(key, secret, encryption_key = nil)
79
+ ApiKeys.new(key, secret, encryption_key)
43
80
  end
44
81
 
45
- def load(file = default_credentials_file)
46
- validate_argument(file)
82
+ def keys_from_file(filename: default_credentials_path,
83
+ account_name: nil,
84
+ encryption_key: nil)
47
85
 
48
- new.tap do |local|
49
- local.merge!(parse_file(file)) if exist?(file)
50
- local.symbolize!
51
- end
86
+ YamlFile.new(filename: filename).keys(account_name: account_name,
87
+ encryption_key: encryption_key)
52
88
  end
53
89
 
54
-
55
- private
56
-
57
- def validate_argument(file)
58
- unless file && File.exist?(file)
59
- raise CredentialsFileNotFound, "File #{file} could not be found"
60
- end
90
+ # @return String path to the default credentials file.
91
+ def default_credentials_path(user: nil)
92
+ user ?
93
+ File.expand_path(Dir.home(user) + '/.dnsmadeeasy/credentials.yml').freeze :
94
+ File.expand_path('~/.dnsmadeeasy/credentials.yml').freeze
61
95
  end
62
-
63
-
64
- def parse_file(file)
65
- YAML.load(read_file(file))
66
- end
67
-
68
-
69
- def read_file(file)
70
- File.read(file)
71
- end
72
- end
73
-
74
- # Set the default
75
- self.default_credentials_file ||= File.expand_path('~/.dnsmadeeasy/credentials.yml').freeze
76
-
77
- # Instance Methods
78
- # NOTE: we are subclassing Hash, which isn't awesome, but gets the job done.
79
-
80
- def symbolize(param_hash = self)
81
- Hash.new.tap { |hash|
82
- param_hash.each_pair do |key, key_value|
83
- value = recurse_if_needed(key_value)
84
- symbolize_key(hash, key, value)
85
- end
86
- }
87
- end
88
-
89
- public
90
-
91
- def valid?
92
- api_key && api_secret
93
96
  end
94
-
95
-
96
- def symbolize!
97
- hash = symbolize(self)
98
- clear
99
- merge!(hash)
100
- end
101
-
102
-
103
- def api_key
104
- credentials && credentials[:api_key]
105
- end
106
-
107
-
108
- def api_secret
109
- credentials && credentials[:api_secret]
110
- end
111
-
112
-
113
- private
114
-
115
- def symbolize_key(hash, key, value)
116
- case key
117
- when String, Symbol
118
- hash[key.to_sym] = value
119
- else
120
- hash[key.to_s.to_sym] = value
121
- end
122
- end
123
-
124
-
125
- def recurse_if_needed(key_value)
126
- key_value.is_a?(Hash) ? symbolize(key_value) : key_value
127
- end
128
-
129
-
130
- def credentials
131
- self[:credentials] || {}
132
- end
133
-
134
-
135
97
  end
136
98
  end
@@ -0,0 +1,77 @@
1
+ require 'yaml'
2
+ require 'dnsmadeeasy'
3
+ require 'hashie/extensions/mash/symbolize_keys'
4
+ require 'sym'
5
+ require 'sym/app'
6
+ require 'digest'
7
+
8
+ module DnsMadeEasy
9
+ module Credentials
10
+ #
11
+ # Immutable instance with key and secret.
12
+ #
13
+ class ApiKeys
14
+ API_KEY_REGEX = /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/
15
+
16
+ attr_reader :api_key,
17
+ :api_secret,
18
+ :encryption_key,
19
+ :default,
20
+ :account_name
21
+
22
+ include Sym
23
+
24
+ def initialize(key, secret, encryption_key = nil, default: false, account_name: nil)
25
+ raise InvalidCredentialKeys, "Key and Secret can not be nil" if key.nil? || secret.nil?
26
+
27
+ @default = default
28
+ @account_name = account_name
29
+
30
+ if !valid?(key, secret) && encryption_key
31
+ @encryption_key = sym_resolve(encryption_key)
32
+ @api_key = decr(key, @encryption_key)
33
+ @api_secret = decr(secret, @encryption_key)
34
+ else
35
+ @api_key = key
36
+ @api_secret = secret
37
+ end
38
+
39
+ raise InvalidCredentialKeys, "Key [#{api_key}] or Secret [#{api_secret}] has failed validation for its format" unless valid?
40
+ end
41
+
42
+ def sym_resolve(encryption_key)
43
+ null_output = ::File.open('/dev/null', 'w')
44
+ result = Sym::Application.new({ cache_passwords: true, key: encryption_key }, $stdin, null_output, null_output).execute
45
+ if result.is_a?(Hash)
46
+ raise InvalidCredentialKeys, "Unable to decrypt the data, error is: #{result[:exception]}"
47
+ else
48
+ result
49
+ end
50
+ end
51
+
52
+ def to_s
53
+ "<#{self.class.name}#key=[s#{rofl(api_key)}] secret=[#{rofl(api_secret)}] encryption_key=[#{rofl(encryption_key)}]>"
54
+ end
55
+
56
+ def rofl(key)
57
+ Digest::SHA256::hexdigest(key) if key
58
+ end
59
+
60
+ def valid?(key = self.api_key, secret = self.api_secret)
61
+ key &&
62
+ secret &&
63
+ API_KEY_REGEX.match(key) &&
64
+ API_KEY_REGEX.match(secret)
65
+ end
66
+
67
+ def ==(other)
68
+ other.is_a?(ApiKeys) &&
69
+ other.valid? &&
70
+ other.api_key == api_key &&
71
+ other.api_secret == api_secret
72
+ end
73
+
74
+ alias eql? ==
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,75 @@
1
+ require 'yaml'
2
+ require 'dnsmadeeasy'
3
+ require 'hashie/extensions/symbolize_keys'
4
+ require 'sym'
5
+ require_relative 'api_keys'
6
+
7
+ module DnsMadeEasy
8
+ module Credentials
9
+
10
+ class YamlFile
11
+ attr_accessor :filename, :account, :mash
12
+
13
+ def initialize(filename: default_credentials_path)
14
+ self.filename = filename
15
+ parse! if exist?
16
+ end
17
+
18
+ def keys(account_name: nil, encryption_key: nil)
19
+ return nil unless exist?
20
+ return nil if mash.nil?
21
+
22
+ creds = if mash.accounts.is_a?(Array)
23
+ account = if account_name
24
+ mash.accounts.find { |a| a.name == account_name.to_s }
25
+ else
26
+ mash.accounts.find { |a| a.default_account }
27
+ end
28
+
29
+ raise DnsMadeEasy::APIKeyAndSecretMissingError,
30
+ (account_name ? "account #{account_name} was not found" : 'default account does not exist') unless account
31
+
32
+ raise DnsMadeEasy::InvalidCredentialsFormatError,
33
+ 'Expected account entry to have "credentials" key' unless account.credentials
34
+
35
+ account.credentials
36
+
37
+ elsif mash.credentials
38
+ mash.credentials
39
+
40
+ else
41
+ raise DnsMadeEasy::InvalidCredentialsFormatError,
42
+ 'expected either "accounts" or "credentials" as the top-level key'
43
+ end
44
+
45
+ creds ? ApiKeys.new(creds.api_key,
46
+ creds.api_secret,
47
+ encryption_key || creds.encryption_key) : nil
48
+ end
49
+
50
+ def to_s
51
+ "file #{filename}"
52
+ end
53
+
54
+ private
55
+
56
+ def parse!
57
+ self.mash = Hashie::Extensions::SymbolizeKeys.symbolize_keys(load_hash)
58
+ end
59
+
60
+ def exist?
61
+ ::File.exist?(filename)
62
+ end
63
+
64
+ def contents
65
+ ::File.read(filename)
66
+ end
67
+
68
+ def load_hash
69
+ Hashie::Mash.new(YAML.load(contents))
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
@@ -8,28 +8,25 @@ require 'dnsmadeeasy/api/client'
8
8
 
9
9
  module DnsMadeEasy
10
10
  class Runner
11
- SUPPORTED_FORMATS = %w(json yaml)
11
+ SUPPORTED_FORMATS = %w(json json_pretty yaml pp)
12
12
 
13
13
  attr_accessor :format, :argv, :operation
14
14
 
15
-
16
15
  def initialize(argv = nil)
17
- self.argv = argv || ARGV.dup
18
- configure_authentication
16
+ self.argv = argv || ARGV.dup
19
17
  self.format = process_flags_format
20
18
  end
21
19
 
22
-
23
20
  def execute!
24
21
  if argv.empty? || argv.size < 1
25
22
  print_help_message
26
23
  else
24
+ configure_authentication
27
25
  self.operation = argv.shift.to_sym
28
26
  exit call_through_client(operation)
29
27
  end
30
28
  end
31
29
 
32
-
33
30
  private
34
31
 
35
32
  def call_through_client(method)
@@ -52,7 +49,10 @@ module DnsMadeEasy
52
49
  print_signature(method, sig) :
53
50
  print_error('Action', "#{method.to_s.bold.yellow}", 'has generated an error'.red, exception: e)
54
51
  1
55
- rescue NoMethodError
52
+ rescue NoMethodError => e
53
+
54
+ puts e.backtrace.reverse.join("\n").red
55
+
56
56
  print_error('Action', "#{method.to_s.bold.yellow}", 'is not valid.'.red)
57
57
  puts 'HINT: try running ' + 'dme operations'.bold.green + ' to see the list of valid operations.'
58
58
  2
@@ -62,7 +62,6 @@ module DnsMadeEasy
62
62
  end
63
63
  end
64
64
 
65
-
66
65
  def print_error(*args, exception: nil)
67
66
  unless args.empty?
68
67
  puts <<-EOF
@@ -77,19 +76,17 @@ module DnsMadeEasy
77
76
  end
78
77
  end
79
78
 
80
-
81
79
  def print_signature(method, sig)
82
80
  puts <<-EOF
83
81
  #{'Error: '.bold.yellow}
84
- #{'You are missing some arguments for this operation:'.red}
82
+ #{'You are missing some arguments for this operation:'.red}
85
83
 
86
- #{'Correct Usage: '.bold.yellow}
87
- #{method.to_s.bold.green} #{sig.join(' ').blue }
84
+ #{'Correct Usage: '.bold.yellow}
85
+ #{method.to_s.bold.green} #{sig.join(' ').blue }
88
86
 
89
87
  EOF
90
88
  end
91
89
 
92
-
93
90
  def process_flags_format
94
91
  if argv.first&.start_with?('--')
95
92
  format = argv.shift.gsub(/^--/, '')
@@ -108,45 +105,64 @@ module DnsMadeEasy
108
105
  format
109
106
  end
110
107
 
111
-
112
108
  def configure_authentication
113
- if ENV['DNSMADEEASY_API_KEY'] && ENV['DNSMADEEASY_API_SECRET']
114
- DnsMadeEasy.configure do |config|
115
- config.api_key = ENV['DNSMADEEASY_API_KEY']
116
- config.api_secret = ENV['DNSMADEEASY_API_SECRET']
117
- end
109
+ keys = DnsMadeEasy::Credentials.keys_from_file(
110
+ filename: ENV['DNSMADEEASY_CREDENTIALS_FILE'] || DnsMadeEasy::Credentials.default_credentials_path(user: ENV['USER'])
111
+ )
112
+ if keys
113
+ DnsMadeEasy.api_key = keys.api_key
114
+ DnsMadeEasy.api_secret = keys.api_secret
118
115
  else
119
- DnsMadeEasy.credentials = (ENV['DNSMADEEASY_CREDENTIALS'] || DnsMadeEasy::Credentials.default_credentials_file)
116
+ raise DnsMadeEasy::APIKeyAndSecretMissingError
120
117
  end
121
- end
118
+ rescue DnsMadeEasy::APIKeyAndSecretMissingError => e
119
+ print_error(e.message)
122
120
 
121
+ puts('You can also set two environment variables: ')
122
+ puts(' DNSMADEEASY_API_KEY and DNSMADEEASY_API_SECRET')
123
+
124
+ exit 123
125
+ end
123
126
 
124
127
  def print_usage_message
125
128
  puts <<-EOF
126
129
  #{'Usage:'.bold.yellow}
127
- #{'# Execute an API call:'.dark}
128
- #{"dme [ #{SUPPORTED_FORMATS.map { |f| "--#{f}" }.join(' | ')} ] operation [ arg1 arg2 ... ] ".bold.green}
130
+ #{'# Execute an API call:'.dark}
131
+ #{"dme [ #{SUPPORTED_FORMATS.map { |f| "--#{f}" }.join(' | ')} ] operation [ arg1 arg2 ... ] ".bold.green}
129
132
 
130
- #{'# Print suported operations:'.dark}
131
- #{'dme op[erations]'.bold.green}
133
+ #{'# Print suported operations:'.dark}
134
+ #{'dme op[erations]'.bold.green}
132
135
 
133
136
  EOF
134
137
  end
135
138
 
136
-
137
139
  def print_help_message
138
140
  print_usage_message
139
141
 
140
142
  puts <<-EOF
141
- #{'Credentials:'.bold.yellow}
143
+ #{header 'Credentials'}
142
144
  Store your credentials in a YAML file
143
- #{DnsMadeEasy::Credentials.default_credentials_file} as follows:
145
+ #{DnsMadeEasy::Credentials.default_credentials_path(user: ENV['USER'])} as follows:
144
146
 
145
147
  #{'credentials:
146
148
  api_key: XXXX
147
149
  api_secret: YYYY'.bold.magenta}
148
150
 
149
- #{'Examples:'.bold.yellow}
151
+ Or a multi-account version:
152
+
153
+ #{'accounts:
154
+ - name: production
155
+ credentials:
156
+ api_key: XXXX
157
+ api_secret: YYYY
158
+ encryption_key: my_key
159
+ - name: development
160
+ default_account: true
161
+ credentials:
162
+ api_key: ZZZZ
163
+ api_secret: WWWW'.bold.magenta}
164
+
165
+ #{header 'Examples:'}
150
166
  #{'dme domain moo.com
151
167
  dme --json domain moo.com
152
168
  dme find_all moo.com A www
@@ -157,30 +173,39 @@ module DnsMadeEasy
157
173
  exit 1
158
174
  end
159
175
 
176
+ def header(message)
177
+ "#{message.bold.yellow}"
178
+ end
160
179
 
161
180
  def print_supported_operations
162
181
  puts <<-EOF
163
- #{'Actions:'.bold.yellow}
182
+ #{header 'Actions:'}
164
183
  Checkout the README and RubyDoc for the arguments to each operation,
165
184
  which is basically a method on a DnsMadeEasy::Api::Client instance.
166
185
  #{'http://www.rubydoc.info/gems/dnsmadeeasy/DnsMadeEasy/Api/Client'.blue.bold.underlined}
167
186
 
168
- #{'Valid Operations Are:'.bold.yellow}
169
- #{DnsMadeEasy::Api::Client.public_operations.join("\n ").green.bold}
187
+ #{header 'Valid Operations Are:'}
188
+ #{DnsMadeEasy::Api::Client.public_operations.join("\n ").green.bold}
170
189
 
171
190
  EOF
172
191
  end
173
192
 
174
-
175
193
  def print_formatted(result, format = nil)
176
194
  if format
177
- puts result.send("to_#{format}".to_sym)
195
+ if format.to_sym == :json_pretty
196
+ puts JSON.pretty_generate(result)
197
+ elsif format.to_sym == :pp
198
+ require 'pp'
199
+ pp result
200
+ else
201
+ m = "to_#{format}".to_sym
202
+ puts result.send(m) if result.respond_to?(m)
203
+ end
178
204
  else
179
205
  ap(result, indent: 10)
180
206
  end
181
207
  end
182
208
 
183
-
184
209
  # e.backtrack.first looks like this:
185
210
  # ..../dnsmadeeasy/lib/dnsmadeeasy/api/client.rb:143:in `create_a_record'
186
211
  def method_signature(e, method)
@@ -1,3 +1,3 @@
1
1
  module DnsMadeEasy
2
- VERSION = '0.2.3'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dnsmadeeasy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
@@ -12,8 +12,22 @@ authors:
12
12
  autorequire:
13
13
  bindir: exe
14
14
  cert_chain: []
15
- date: 2017-12-14 00:00:00.000000000 Z
15
+ date: 2017-12-23 00:00:00.000000000 Z
16
16
  dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: sym
19
+ requirement: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
17
31
  - !ruby/object:Gem::Dependency
18
32
  name: hashie
19
33
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +196,7 @@ executables:
182
196
  extensions: []
183
197
  extra_rdoc_files: []
184
198
  files:
199
+ - ".codeclimate.yml"
185
200
  - ".gitignore"
186
201
  - ".rspec"
187
202
  - ".travis.yml"
@@ -197,6 +212,8 @@ files:
197
212
  - lib/dnsmadeeasy.rb
198
213
  - lib/dnsmadeeasy/api/client.rb
199
214
  - lib/dnsmadeeasy/credentials.rb
215
+ - lib/dnsmadeeasy/credentials/api_keys.rb
216
+ - lib/dnsmadeeasy/credentials/yaml_file.rb
200
217
  - lib/dnsmadeeasy/dme.rb
201
218
  - lib/dnsmadeeasy/runner.rb
202
219
  - lib/dnsmadeeasy/version.rb