enfcli 3.3.2.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
data/enfcli.gemspec ADDED
@@ -0,0 +1,46 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'enfcli/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "enfcli"
8
+ spec.version = EnfCli::VERSION
9
+ spec.authors = ["Venkatakumar Srinivasan"]
10
+ spec.email = ["venkat@xaptum.com"]
11
+
12
+ spec.summary = %q{Xaptum ENF CLI.}
13
+ spec.homepage = "https://www.xaptum.com"
14
+
15
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
17
+ #if spec.respond_to?(:metadata)
18
+ # spec.metadata['allowed_push_host'] = "https://gems.xaptum.xyz"
19
+ #else
20
+ # raise "RubyGems 2.0 or newer is required to protect against " \
21
+ # "public gem pushes."
22
+ #end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(%r{^(test|spec|features)/})
26
+ end
27
+ spec.bindir = "bin"
28
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency 'thor', '~> 0.20.0'
32
+ spec.add_dependency "rest-client", "~> 2.0"
33
+ spec.add_dependency "terminal-table"
34
+ #spec.add_dependency "websocket-client-simple"
35
+ #spec.add_dependency "colorize"
36
+
37
+ spec.add_development_dependency "bundler"
38
+ spec.add_development_dependency "rake", "~> 10.0"
39
+ spec.add_development_dependency "bump"
40
+ spec.add_development_dependency "rspec"
41
+ spec.add_development_dependency "rspec_junit_formatter"
42
+ spec.add_development_dependency "fuubar"
43
+ spec.add_development_dependency "vcr"
44
+ spec.add_development_dependency "webmock"
45
+
46
+ end
data/lib/enfapi.rb ADDED
@@ -0,0 +1,398 @@
1
+ #
2
+ # Copyright 2018 Xaptum,Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ require 'rest-client'
17
+ require 'singleton'
18
+ require 'json'
19
+ require 'date'
20
+ require 'base64'
21
+
22
+ #
23
+ # NOTE: When api's are refactored into individual classes
24
+ # please be sure to override to_json method of the Singleton
25
+ # class OR replace all calls to to_json() with EnfApi::to_json()
26
+ #
27
+
28
+ module EnfApi
29
+
30
+ def self.to_json(hash)
31
+ JSON.generate(hash)
32
+ end
33
+
34
+ class ERROR < StandardError
35
+ def initialize(msg = "ENF Api Error")
36
+ super
37
+ end
38
+ end
39
+
40
+ class Firewall
41
+ include Singleton
42
+
43
+ def list_firewall_rules(network)
44
+ EnfApi::API.instance.get "/api/xfw/v1/#{network}/rule"
45
+ end
46
+
47
+ def add_firewall_rule(network, rule)
48
+ rule_json = EnfApi::to_json(rule)
49
+ EnfApi::API.instance.post "/api/xfw/v1/#{network}/rule", rule_json
50
+ end
51
+
52
+ def delete_firewall_rules(network, id = nil)
53
+ # Same method to call to delete all firewall rules in a network. if id is nil
54
+ EnfApi::API.instance.delete "/api/xfw/v1/#{network}/rule/#{id}"
55
+ end
56
+ end
57
+
58
+ class Iam
59
+ include Singleton
60
+
61
+ ## NO_TEST
62
+ def provision_group(new_group)
63
+ json = EnfApi::to_json(new_group)
64
+ EnfApi::API.instance.post "/api/xiam/v1/groups", json
65
+ end
66
+
67
+ ## NO_TEST
68
+ def update_group(gid, modified_group)
69
+ json = EnfApi::to_json(modified_group)
70
+ EnfApi::API.instance.put "/api/xiam/v1/groups/#{gid}", json
71
+ end
72
+
73
+ ## NO_TEST
74
+ def add_group_to_network(gid, ipv6_network)
75
+ json = EnfApi::to_json(ipv6_network)
76
+ EnfApi::API.instance.post "/api/xiam/v1/groups/#{gid}/networks", json
77
+ end
78
+
79
+ ## NO_TEST
80
+ def list_network_groups(network)
81
+ EnfApi::API.instance.get "/api/xiam/v1/networks/#{network}/groups"
82
+ end
83
+
84
+ ## NO_TEST
85
+ def list_group(gid)
86
+ EnfApi::API.instance.get "/api/xiam/v1/groups/#{gid}"
87
+ end
88
+
89
+ ## NO_TEST
90
+ def provision_endpoint(new_endpoint)
91
+ json = EnfApi::to_json(new_endpoint)
92
+ EnfApi::API.instance.post "/api/xiam/v1/endpoints", json
93
+ end
94
+
95
+ ## NO_TEST
96
+ def update_endpoint_key(ipv6, credentials)
97
+ json = EnfApi::to_json(credentials)
98
+ EnfApi::API.instance.post "/api/xiam/v1/endpoints/#{ipv6}/credentials", json
99
+ end
100
+
101
+ end
102
+
103
+ class NetworkManager
104
+ include Singleton
105
+ end
106
+
107
+ class API
108
+ include Singleton
109
+
110
+ attr_accessor :headers, :api
111
+
112
+ def authenticate(host, user, password)
113
+ # format url
114
+ host = "https://#{host}" unless host =~ /^(http|https):\/\//
115
+
116
+ # chomp trailing '/'
117
+ host = host.chomp("/")
118
+
119
+ # Initalize rest client
120
+ @api = RestClient::Resource.new(host)
121
+
122
+ # Create request json
123
+ hash = { :username => user, :token => password }
124
+ json = to_json( hash )
125
+
126
+ # call api
127
+ @api["/api/xcr/v2/xauth"].post( json, {content_type: :json, accept: :json}) { |response, request, result|
128
+ case response.code
129
+ when 200
130
+ # get resp from json
131
+ resp = from_json(response.body)
132
+
133
+ # set request headers for subsequent api calls
134
+ token = resp[:data][0][:token]
135
+ @headers = {content_type: :json, accept: :json, "Authorization" => "Bearer #{token}"}
136
+
137
+ # return resp
138
+ resp
139
+ else
140
+ raise EnfApi::ERROR, "Login Failed! Invalid user or password!"
141
+ end
142
+ }
143
+ end
144
+
145
+ def get(request_uri)
146
+ @api[request_uri].get(@headers) {|response, request, result|
147
+ process_api_response response, request, result
148
+ }
149
+ end
150
+
151
+ def post(request_uri, request_body = '')
152
+ @api[request_uri].post(request_body, @headers) {|response, request, result|
153
+ process_api_response response, request, result
154
+ }
155
+ end
156
+
157
+ def put(request_uri, request_body = '')
158
+ @api[request_uri].put(request_body, @headers) {|response, request, result|
159
+ process_api_response response, request, result
160
+ }
161
+ end
162
+
163
+ def delete(request_uri)
164
+ @api[request_uri].delete(@headers) {|response, request, result|
165
+ process_api_response response, request, result
166
+ }
167
+ end
168
+
169
+ ############################################################################################################################
170
+ # NOT READY API. MOVE ABOVE THIS LINE AFTER RSPEC OR INTO OWN CLASS
171
+ ############################################################################################################################
172
+ def list_domain_nws(domain_id)
173
+ @api["/api/xcr/v2/domains/#{domain_id}/nws"].get(@headers) {|response, request, result|
174
+ process_api_response response, request, result
175
+ }
176
+ end
177
+
178
+ def list_enfnws
179
+ @api["/api/xcr/v2/enfnws"].get( @headers) {|response, request, result|
180
+ process_api_response response, request, result
181
+ }
182
+ end
183
+
184
+ def list_domains
185
+ @api["/api/xcr/v2/domains"].get( @headers) {|response, request, result|
186
+ process_api_response response, request, result
187
+ }
188
+ end
189
+
190
+ def get_domain(domain_id)
191
+ @api["/api/xcr/v2/domains/#{domain_id}"].get( @headers) {|response, request, result|
192
+ process_api_response response, request, result
193
+ }
194
+ end
195
+
196
+ def create_domain(domain_hash)
197
+ json = to_json( domain_hash )
198
+ @api["/api/xcr/v2/domains"].post( json, @headers) {|response, request, result|
199
+ process_api_response response, request, result
200
+ }
201
+ end
202
+
203
+ def update_domain_rate_limits(domain_network, limit, limits_hash)
204
+ json = to_json( limits_hash )
205
+ @api["/api/xcr/v2/domains/#{domain_network}/ep_rate_limits/#{limit}"].put( json, @headers) {|response, request, result|
206
+ process_api_response response, request, result
207
+ }
208
+ end
209
+
210
+ def update_network_rate_limits(network, limit, limits_hash)
211
+ json = to_json( limits_hash )
212
+ @api["/api/xcr/v2/nws/#{network}/ep_rate_limits/#{limit}"].put( json, @headers) {|response, request, result|
213
+ process_api_response response, request, result
214
+ }
215
+ end
216
+
217
+ def update_ep_rate_limits(ipv6, limit, limits_hash)
218
+ json = to_json( limits_hash )
219
+ @api["/api/xcr/v2/cxns/#{ipv6}/ep_rate_limits/#{limit}"].put( json, @headers) {|response, request, result|
220
+ process_api_response response, request, result
221
+ }
222
+ end
223
+
224
+ def get_domain_rate_limits(domain_network, filter = nil)
225
+ api_url = "/api/xcr/v2/domains/#{domain_network}/ep_rate_limits"
226
+ api_url = "#{api_url}/#{filter}" if filter
227
+
228
+ @api[api_url].get( @headers) {|response, request, result|
229
+ process_api_response response, request, result
230
+ }
231
+ end
232
+
233
+ def get_network_rate_limits(network, filter = nil)
234
+ api_url = "/api/xcr/v2/nws/#{network}/ep_rate_limits"
235
+ api_url = "#{api_url}/#{filter}" if filter
236
+
237
+ @api[api_url].get( @headers) {|response, request, result|
238
+ process_api_response response, request, result
239
+ }
240
+ end
241
+
242
+ def get_ep_rate_limits(ipv6, filter = nil)
243
+ api_url = "/api/xcr/v2/cxns/#{ipv6}/ep_rate_limits"
244
+ api_url = "#{api_url}/#{filter}" if filter
245
+
246
+ @api[api_url].get( @headers) {|response, request, result|
247
+ process_api_response response, request, result
248
+ }
249
+ end
250
+
251
+ def activate_domain(domain_network)
252
+ @api["/api/xcr/v2/domains/#{domain_network}/status"].put( '', @headers) {|response, request, result|
253
+ process_api_response response, request, result
254
+ }
255
+ end
256
+
257
+ def deactivate_domain(domain_network)
258
+ @api["/api/xcr/v2/domains/#{domain_network}/status"].delete( @headers) {|response, request, result|
259
+ process_api_response response, request, result
260
+ }
261
+ end
262
+
263
+ def create_nw(nw_hash)
264
+ json = to_json(nw_hash)
265
+ domain_id = nw_hash[:domain_id]
266
+ @api["/api/xcr/v2/domains/#{domain_id}/nws"].post(json, @headers) {|response, request, result|
267
+ process_api_response response, request, result
268
+ }
269
+ end
270
+
271
+ def list_nw_connections(domain_id, network_cidr, file = nil)
272
+ @api["/api/xcr/v2/domains/#{domain_id}/nws/#{network_cidr}/cxns"].get(@headers) {|response, request, result|
273
+ process_api_response response, request, result
274
+ }
275
+ end
276
+
277
+ def list_endpoint_events(ipv6)
278
+ @api["/api/xcr/v2/cxns/#{ipv6}/events"].get(@headers) {|response, request, result|
279
+ process_api_response response, request, result
280
+ }
281
+ end
282
+
283
+ def list_network_events(network_cidr)
284
+ @api["/api/xcr/v2/nws/#{network_cidr}/events"].get(@headers) {|response, request, result|
285
+ process_api_response response, request, result
286
+ }
287
+ end
288
+
289
+ def get_endpoint(ipv6)
290
+ @api["/api/xcr/v2/cxns/#{ipv6}"].get(@headers) {|response, request, result|
291
+ process_api_response response, request, result
292
+ }
293
+ end
294
+
295
+ def update_endpoint(ipv6, name)
296
+ hash = { :ipv6 => ipv6, :name => name }
297
+ json = to_json( hash )
298
+
299
+ @api["/api/xcr/v2/cxns/#{ipv6}"].put( json, @headers) {|response, request, result|
300
+ process_api_response response, request, result
301
+ }
302
+ end
303
+
304
+ def activate_enfnw(subnet)
305
+ @api["/api/xcr/v2/enfnws/#{subnet}"].put( '', @headers) {|response, request, result|
306
+ process_api_response response, request, result
307
+ }
308
+ end
309
+
310
+ def list_domain_users(domain_network)
311
+ @api["/api/xcr/v2/domains/#{domain_network}/users"].get( @headers) {|response, request, result|
312
+ puts response.code
313
+ process_api_response response, request, result
314
+ }
315
+ end
316
+
317
+ def list_domain_invites(domain_network)
318
+ @api["/api/xcr/v2/domains/#{domain_network}/invites"].get(@headers) {|response, request, result|
319
+ process_api_response response, request, result
320
+ }
321
+ end
322
+
323
+ def invite(domain_network, hash)
324
+ json = to_json( hash )
325
+ @api["/api/xcr/v2/domains/#{domain_network}/invites"].post( json, @headers) { |response, request, result|
326
+ process_api_response response, request, result
327
+ }
328
+ end
329
+
330
+ def cancel_invite(email)
331
+ @api["/api/xcr/v2/invites/#{email}"].delete( @headers) {|response, request, result|
332
+ process_api_response response, request, result
333
+ }
334
+ end
335
+
336
+ def resend_invite(email)
337
+ @api["/api/xcr/v2/invites/#{email}"].put( "{}", @headers) {|response, request, result|
338
+ process_api_response response, request, result
339
+ }
340
+ end
341
+
342
+ ############################################################################################################################
343
+ # Private functions
344
+ ############################################################################################################################
345
+ def process_api_response(response, request, result)
346
+ case response.code
347
+ when 200
348
+ # return json
349
+ from_json(response.body)
350
+
351
+ when 201
352
+ # return response body
353
+ from_json(response.body)
354
+
355
+ when 400
356
+ # api returns and error
357
+ raise EnfApi::ERROR, api_error_msg(from_json(response.body))
358
+
359
+ when 401
360
+ # api returns and error
361
+ raise EnfApi::ERROR, api_error_msg(from_json(response.body))
362
+
363
+ when 403
364
+ # api returns and error
365
+ raise EnfApi::ERROR, "AUTHORIZATION_ERROR: User is not authorized to perform this operation!"
366
+
367
+ else
368
+ raise EnfApi::ERROR, "Unexpected error! Please try again!"
369
+ end
370
+ end
371
+
372
+ def from_json(string)
373
+ begin
374
+ JSON.parse(string, {:symbolize_names => true})
375
+ rescue
376
+ raise EnfApi::ERROR, "Unable to parse api response json!"
377
+ end
378
+ end
379
+
380
+ def to_json(hash)
381
+ JSON.generate(hash)
382
+ end
383
+
384
+ def api_error_msg(err)
385
+ if err.key?(:xiam_error)
386
+ reason = err[:reason]
387
+ reason = Base64.decode64(reason)
388
+ "#{reason}"
389
+ else
390
+ code = err[:error][:code]
391
+ text = err[:error][:text]
392
+ "#{code.upcase}: #{text}"
393
+ end
394
+ end
395
+
396
+ private :from_json, :to_json, :api_error_msg, :process_api_response
397
+ end
398
+ end
@@ -0,0 +1,185 @@
1
+ #
2
+ # Copyright 2018 Xaptum,Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ require 'enfthor'
17
+ require 'enfapi'
18
+
19
+ module EnfCli
20
+
21
+ module Cmd
22
+
23
+ class User < EnfThor
24
+ no_commands {
25
+ def display_invites invites
26
+ headings = ['Id', 'User Name', 'Full Name', 'Invited By', 'Invite Code']
27
+ rows = invites.map{ |hash|
28
+ [ hash[:id], hash[:email], hash[:name], hash[:invited_by], hash[:invite_token] ]
29
+ }
30
+
31
+ render_table(headings, rows)
32
+ end
33
+
34
+ def display_users users
35
+ headings = ['User Name', 'Full Name', 'Last Login', 'Type', 'Reset Code', 'Reset Time']
36
+ rows = users.map{ |hash|
37
+ [ hash[:username], hash[:full_name], hash[:last_login], hash[:type], hash[:reset_code], format_date(hash[:reset_time])]
38
+ }
39
+ render_table(headings, rows)
40
+ end
41
+
42
+ def send_invite options, user_type
43
+ # Get options
44
+ domain_network = options.domain
45
+
46
+ # Get user role
47
+ user_role = EnfCli::CTX.instance.session[:type]
48
+
49
+ # check user roles
50
+ if user_role == "XAPTUM_ADMIN"
51
+ # make sure that a domain is specified in domain option for XAPTUM_ADMIN
52
+ raise EnfCli::ERROR, "No domain specified. Please use --domain option!" unless domain_network
53
+ else
54
+ # use the domain network of the user
55
+ domain_network = EnfCli::CTX.instance.session[:domain_network]
56
+ raise EnfCli::ERROR, "User not in a valid domain!" unless domain_network
57
+ end
58
+
59
+ # get params
60
+ case user_type
61
+ when "DOMAIN_USER"
62
+ name = options[:'name'].join(" ").gsub(/\A"+(.*?)"+\Z/m, '\1')
63
+ email = options[:'email']
64
+
65
+ when "DOMAIN_ADMIN"
66
+ name = options[:'admin-name'].join(" ").gsub(/\A"+(.*?)"+\Z/m, '\1')
67
+ email = options[:'admin-email']
68
+
69
+ end
70
+
71
+ # call api
72
+ hash = { :email => email, :full_name => name, :welcome_text => "", :user_type => user_type }
73
+ data = EnfApi::API.instance.invite domain_network, hash
74
+ invite = data[:data]
75
+ display_invites invite
76
+ end
77
+ }
78
+
79
+ desc "invite-read-only-user", "Invite a domain user"
80
+ method_option :domain, :default => nil, :type => :string, :aliases => "-d"
81
+ method_option :'name', :type => :array, :required => true, :banner => "NAME"
82
+ method_option :'email', :type => :string, :required => true, :banner => "EMAIL"
83
+ def invite_read_only_user
84
+ try_with_rescue_in_session do
85
+ send_invite options, "DOMAIN_USER"
86
+ end
87
+ end
88
+
89
+ desc "invite-admin-user", "Invite a domain administrator"
90
+ method_option :domain, :default => nil, :type => :string, :aliases => "-d"
91
+ method_option :'admin-name', :type => :array, :required => true, :banner => "NAME"
92
+ method_option :'admin-email', :type => :string, :required => true, :banner => "EMAIL"
93
+ def invite_admin_user
94
+ try_with_rescue_in_session do
95
+ send_invite options, "DOMAIN_ADMIN"
96
+ end
97
+ end
98
+
99
+
100
+ desc "cancel-user-invite", "Cancel an invite"
101
+ method_option :email, :type => :string, :required => true
102
+ def cancel_user_invite
103
+ try_with_rescue_in_session do
104
+ # call api
105
+ EnfApi::API.instance.cancel_invite options.email
106
+
107
+ # print success
108
+ say "Invite Canceled!", :green
109
+ end
110
+ end
111
+
112
+ desc "resend-user-invite", "Resend an invite"
113
+ method_option :email, :type => :string, :required => true
114
+ def resend_user_invite
115
+ try_with_rescue_in_session do
116
+ # call api
117
+ EnfApi::API.instance.resend_invite options.email
118
+
119
+ # print success
120
+ say "Resent invite email!", :green
121
+ end
122
+ end
123
+
124
+ desc "list-domain-invites", "List domain invites"
125
+ method_option :domain, :default => nil, :type => :string, :aliases => "-d"
126
+ def list_domain_invites
127
+ try_with_rescue_in_session do
128
+ # Get options
129
+ domain_network = options.domain
130
+
131
+ # Get user role
132
+ user_role = EnfCli::CTX.instance.session[:type]
133
+
134
+ # check user roles
135
+ if user_role == "XAPTUM_ADMIN"
136
+ # make sure that a domain is specified in domain option for XAPTUM_ADMIN
137
+ raise EnfCli::ERROR, "No domain specified. Please use --domain option!" unless domain_network
138
+ else
139
+ # use the domain network of the user
140
+ domain_network = EnfCli::CTX.instance.session[:domain_network]
141
+ raise EnfCli::ERROR, "User not in a valid domain!" unless domain_network
142
+ end
143
+
144
+ # call the api
145
+ data = EnfApi::API.instance.list_domain_invites domain_network
146
+ invites = data[:data]
147
+
148
+ display_invites invites
149
+ end
150
+ end
151
+
152
+ desc "list-domain-users", "List domain users"
153
+ method_option :domain, :default => nil, :type => :string, :aliases => "-d"
154
+ def list_domain_users
155
+ try_with_rescue_in_session do
156
+ # Get options
157
+ domain_network = options.domain
158
+
159
+ # Get user role
160
+ user_role = EnfCli::CTX.instance.session[:type]
161
+
162
+ # check user roles
163
+ if user_role == "XAPTUM_ADMIN"
164
+ # make sure that a domain is specified in domain option for XAPTUM_ADMIN
165
+ raise EnfCli::ERROR, "No domain specified. Please use --domain option!" unless domain_network
166
+ else
167
+ # use the domain network of the user
168
+ domain_network = EnfCli::CTX.instance.session[:domain_network]
169
+ raise EnfCli::ERROR, "User not in a valid domain!" unless domain_network
170
+ end
171
+
172
+ # call the api
173
+ data = EnfApi::API.instance.list_domain_users domain_network
174
+ users = data[:data]
175
+
176
+ display_users users
177
+ end
178
+ end
179
+
180
+
181
+ end
182
+
183
+ end
184
+
185
+ end