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 +4 -4
- data/.codeclimate.yml +30 -0
- data/README.md +104 -26
- data/Rakefile +8 -0
- data/dnsmadeeasy.gemspec +1 -0
- data/lib/dnsmadeeasy.rb +48 -32
- data/lib/dnsmadeeasy/credentials.rb +72 -110
- data/lib/dnsmadeeasy/credentials/api_keys.rb +77 -0
- data/lib/dnsmadeeasy/credentials/yaml_file.rb +75 -0
- data/lib/dnsmadeeasy/runner.rb +60 -35
- data/lib/dnsmadeeasy/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f567aee61ac65f6e851a7499a66f0da8639d1d9
|
4
|
+
data.tar.gz: 44825d6fcde3259791f194696da581ebbebc658c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3935c9a7b0211ca35321ce1040352e12d8df876d8f1cc4b049bd987a711596cc353c5caa57a47b2dd97b3d4d7569f58a5da4e47b2f83351527c0340db94a9545
|
7
|
+
data.tar.gz: 70d6e6a69d8e8f66e5875d45dba02ac6ac50da4092ba4e1da195ac525d35e894fa01cf439f54576e1883003c2f1ee9877bb96f1b3277ece96b692b3fe5cd8246
|
data/.codeclimate.yml
ADDED
@@ -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.
|
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
|
-
|
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
|
-
|
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
|
-
> *
|
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
|
+
|
data/dnsmadeeasy.gemspec
CHANGED
@@ -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'
|
data/lib/dnsmadeeasy.rb
CHANGED
@@ -7,36 +7,72 @@ require 'dnsmadeeasy/credentials'
|
|
7
7
|
require 'dnsmadeeasy/api/client'
|
8
8
|
|
9
9
|
module DnsMadeEasy
|
10
|
-
class Error < StandardError;
|
11
|
-
|
12
|
-
class
|
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 :
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
22
|
-
config.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
|
28
|
-
|
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
|
-
#
|
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
|
-
#
|
29
|
+
# #### From a default filename ~/.dnsmadeeasy/credentials.yml
|
7
30
|
#
|
8
|
-
#
|
31
|
+
# @creds = DnsMadeEasy::Credentials.keys_from_file
|
9
32
|
#
|
10
|
-
#
|
11
|
-
# creds = DnsMadeEasy::Credentials.load
|
33
|
+
# ### From a multi-account file
|
12
34
|
#
|
13
|
-
#
|
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
|
-
#
|
55
|
+
# ```
|
18
56
|
#
|
19
|
-
#
|
20
|
-
#
|
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
|
-
#
|
23
|
-
#
|
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
|
-
|
28
|
-
|
70
|
+
#
|
71
|
+
# )
|
29
72
|
|
30
|
-
|
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
|
-
|
42
|
-
|
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
|
46
|
-
|
82
|
+
def keys_from_file(filename: default_credentials_path,
|
83
|
+
account_name: nil,
|
84
|
+
encryption_key: nil)
|
47
85
|
|
48
|
-
new.
|
49
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
data/lib/dnsmadeeasy/runner.rb
CHANGED
@@ -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
|
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
|
-
|
82
|
+
#{'You are missing some arguments for this operation:'.red}
|
85
83
|
|
86
|
-
#{'Correct Usage: '.bold.yellow}
|
87
|
-
|
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
|
-
|
114
|
-
DnsMadeEasy.
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
116
|
+
raise DnsMadeEasy::APIKeyAndSecretMissingError
|
120
117
|
end
|
121
|
-
|
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
|
-
|
128
|
-
|
130
|
+
#{'# Execute an API call:'.dark}
|
131
|
+
#{"dme [ #{SUPPORTED_FORMATS.map { |f| "--#{f}" }.join(' | ')} ] operation [ arg1 arg2 ... ] ".bold.green}
|
129
132
|
|
130
|
-
|
131
|
-
|
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
|
143
|
+
#{header 'Credentials'}
|
142
144
|
Store your credentials in a YAML file
|
143
|
-
#{DnsMadeEasy::Credentials.
|
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
|
-
|
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:'
|
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:'
|
169
|
-
|
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
|
-
|
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)
|
data/lib/dnsmadeeasy/version.rb
CHANGED
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.
|
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-
|
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
|