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 +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
|