dnsmadeeasy 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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