cyclid-client 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'colorize'
16
+
17
+ # Cyclid top level module
18
+ module Cyclid
19
+ module Cli
20
+ # 'user' sub-command
21
+ class User < Thor
22
+ desc 'show', 'Show your user details'
23
+ def show
24
+ user = client.user_get(client.config.username)
25
+
26
+ # Pretty print the user details
27
+ puts 'Username: '.colorize(:cyan) + user['username']
28
+ puts 'Name: '.colorize(:cyan) + (user['name'] || '')
29
+ puts 'Email: '.colorize(:cyan) + user['email']
30
+ puts 'Organizations'.colorize(:cyan)
31
+ if user['organizations'].any?
32
+ user['organizations'].each do |org|
33
+ puts "\t#{org}"
34
+ end
35
+ else
36
+ puts "\tNone"
37
+ end
38
+ rescue StandardError => ex
39
+ abort "Failed to get user: #{ex}"
40
+ end
41
+
42
+ desc 'modify', 'Modify your user'
43
+ long_desc <<-LONGDESC
44
+ Modify your user details.
45
+
46
+ The --email option sets your email address.
47
+
48
+ The --password option sets an encrypted password for HTTP Basic authentication and Cyclid
49
+ UI console logins.
50
+
51
+ The --secret option sets a shared secret which is used for signing Cyclid API requests.
52
+ LONGDESC
53
+ option :email, aliases: '-e'
54
+ option :password, aliases: '-p'
55
+ option :secret, aliases: '-s'
56
+ def modify
57
+ client.user_modify(client.config.username,
58
+ email: options[:email],
59
+ password: options[:password],
60
+ secret: options[:secret])
61
+ rescue StandardError => ex
62
+ abort "Failed to modify user: #{ex}"
63
+ end
64
+
65
+ desc 'passwd', 'Change your password'
66
+ def passwd
67
+ # Get the new password
68
+ print 'Password: '
69
+ password = STDIN.noecho(&:gets).chomp
70
+ print "\nConfirm password: "
71
+ confirm = STDIN.noecho(&:gets).chomp
72
+ print "\n"
73
+ abort 'Passwords do not match' unless password == confirm
74
+
75
+ # Modify the user with the new password
76
+ begin
77
+ client.user_modify(client.config.username, password: password)
78
+ rescue StandardError => ex
79
+ abort "Failed to modify user: #{ex}"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,98 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'securerandom'
16
+ require 'oj'
17
+ require 'yaml'
18
+ require 'logger'
19
+
20
+ require 'cyclid/config'
21
+ require 'cyclid/auth_methods'
22
+
23
+ require 'cyclid/client/api'
24
+ require 'cyclid/client/user'
25
+ require 'cyclid/client/organization'
26
+ require 'cyclid/client/job'
27
+ require 'cyclid/client/stage'
28
+ require 'cyclid/client/auth'
29
+ require 'cyclid/client/health'
30
+
31
+ module Cyclid
32
+ # Cyclid client methods
33
+ module Client
34
+ # Tilapia is the standard Cyclid Ruby client. It provides an inteligent
35
+ # programmable API on top of the standard Cyclid REST API, complete with
36
+ # automatic signing of HTTP requests and HTTP error handling.
37
+ #
38
+ # The client provides interfaces for managing Users, Organizations, Stages
39
+ # & Jobs. Refer to the documentation for those modules for more information.
40
+ #
41
+ # In case you're wondering, this class required a name: it couldn't be
42
+ # 'Cyclid' and it couldn't be 'Client'. Tilapia are a common type of
43
+ # Cichlid...
44
+ class Tilapia
45
+ attr_reader :config, :logger
46
+ # @!attribute [r] config
47
+ # @return [Config] Client configuration object
48
+ # @!attribute [r] logger
49
+ # @return [Logger] Client logger object
50
+
51
+ # @param [Hash] options
52
+ # @option options [String] :config_path Fully qualified path to the configuration file
53
+ # @option options [FixNum] :log_level Logger output level
54
+ def initialize(options)
55
+ @config = Config.new(options)
56
+
57
+ # Create a logger
58
+ log_level = options[:log_level] || Logger::FATAL
59
+ @logger = Logger.new(STDERR)
60
+ @logger.level = log_level
61
+
62
+ # Select the API methods to use
63
+ @api = case @config.auth
64
+ when AuthMethods::AUTH_NONE
65
+ Api::None.new(@config, @logger)
66
+ when AuthMethods::AUTH_HMAC
67
+ Api::Hmac.new(@config, @logger)
68
+ when AuthMethods::AUTH_BASIC
69
+ Api::Basic.new(@config, @logger)
70
+ when AuthMethods::AUTH_TOKEN
71
+ Api::Token.new(@config, @logger)
72
+ end
73
+ end
74
+
75
+ include User
76
+ include Organization
77
+ include Job
78
+ include Stage
79
+ include Auth
80
+ include Health
81
+
82
+ private
83
+
84
+ # Build a URI for the configured server & required resource
85
+ def server_uri(path)
86
+ URI::HTTP.build(host: @config.server,
87
+ port: @config.port,
88
+ path: path)
89
+ end
90
+
91
+ def method_missing(method, *args, &block)
92
+ @api.send(method, *args, &block)
93
+ end
94
+ end
95
+
96
+ include AuthMethods
97
+ end
98
+ end
@@ -0,0 +1,114 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'uri'
16
+ require 'net/http'
17
+
18
+ module Cyclid
19
+ # Cyclid client methods
20
+ module Client
21
+ # Client API request methods
22
+ module Api
23
+ # Base class for API request implementations
24
+ class Base
25
+ def initialize(config, logger)
26
+ @config = config
27
+ @logger = logger
28
+ end
29
+
30
+ # Add authentication details & perform a GET request
31
+ def api_get(uri)
32
+ req = authenticate_request(Net::HTTP::Get.new(uri), uri)
33
+
34
+ api_request(uri, req)
35
+ end
36
+
37
+ # Add authentication details perform a POST request with a pre-formatted body
38
+ def api_raw_post(uri, data, content_type)
39
+ unsigned = Net::HTTP::Post.new(uri)
40
+ unsigned.content_type = content_type
41
+ unsigned.body = data
42
+
43
+ req = authenticate_request(unsigned, uri)
44
+
45
+ api_request(uri, req)
46
+ end
47
+
48
+ # Add authentication details perform a POST request with a JSON body
49
+ def api_json_post(uri, data)
50
+ json = Oj.dump(data)
51
+ api_raw_post(uri, json, 'application/json')
52
+ end
53
+
54
+ # Add authentication details perform a POST request with a YAML body
55
+ def api_yaml_post(uri, data)
56
+ yaml = YAML.dump(data)
57
+ api_raw_post(uri, yaml, 'application/x-yaml')
58
+ end
59
+
60
+ # Add authentication details perform a PUT request with a JSON body
61
+ def api_json_put(uri, data)
62
+ unsigned = Net::HTTP::Put.new(uri)
63
+ unsigned.content_type = 'application/json'
64
+ unsigned.body = Oj.dump(data)
65
+
66
+ req = authenticate_request(unsigned, uri)
67
+
68
+ api_request(uri, req)
69
+ end
70
+
71
+ # Add authentication details perform a DELETE request
72
+ def api_delete(uri)
73
+ req = authenticate_request(Net::HTTP::Delete.new(uri), uri)
74
+
75
+ api_request(uri, req)
76
+ end
77
+
78
+ # Perform an API HTTP request & return the parsed response body
79
+ def api_request(uri, req)
80
+ http = Net::HTTP.new(uri.hostname, uri.port)
81
+ res = http.request(req)
82
+
83
+ parse_response(res)
84
+ end
85
+
86
+ # Parse, validate & handle response data
87
+ def parse_response(res)
88
+ response_data = Oj.load(res.body)
89
+
90
+ # Return immediately if the response was an HTTP 200 OK
91
+ return response_data if res.code == '200'
92
+
93
+ @logger.info "server request failed with error ##{res.code}: \
94
+ #{response_data['description']}"
95
+ raise response_data['description']
96
+ rescue Oj::ParseError => ex
97
+ @logger.debug "body: #{res.body}\n#{ex}"
98
+ raise 'failed to decode server response body'
99
+ end
100
+
101
+ # The API does not support non-authenticated requests, so this method
102
+ # must be implemented.
103
+ def authenticate_request(_request, _uri)
104
+ raise NotImplementedError
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ require 'cyclid/client/api/none'
112
+ require 'cyclid/client/api/hmac'
113
+ require 'cyclid/client/api/basic'
114
+ require 'cyclid/client/api/token'
@@ -0,0 +1,30 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Cyclid
16
+ # Cyclid client methods
17
+ module Client
18
+ # Client API HTTP methods
19
+ module Api
20
+ # HTTP Basic auth HTTP methods
21
+ class Basic < Base
22
+ # Add the username & password to the request
23
+ def authenticate_request(request, _uri)
24
+ request.basic_auth(@config.username, @config.password)
25
+ return request
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,59 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'cyclid/hmac'
16
+
17
+ module Cyclid
18
+ # Cyclid client methods
19
+ module Client
20
+ # Client API HTTP methods
21
+ module Api
22
+ # HMAC signed HTTP methods
23
+ class Hmac < Base
24
+ # Sign the request with HMAC
25
+ def authenticate_request(request, uri)
26
+ algorithm = 'sha256'
27
+ signer = Cyclid::HMAC::Signer.new(algorithm)
28
+
29
+ method = if request.is_a? Net::HTTP::Get
30
+ 'GET'
31
+ elsif request.is_a? Net::HTTP::Post
32
+ 'POST'
33
+ elsif request.is_a? Net::HTTP::Put
34
+ 'PUT'
35
+ elsif request.is_a? Net::HTTP::Delete
36
+ 'DELETE'
37
+ else
38
+ raise "invalid request method #{request.inspect}"
39
+ end
40
+
41
+ nonce = SecureRandom.hex
42
+ headers = signer.sign_request(uri.path,
43
+ @config.secret,
44
+ auth_header_format: '%{auth_scheme} %{username}:%{signature}', # rubocop:disable Metrics/LineLength
45
+ username: @config.username,
46
+ nonce: nonce,
47
+ method: method)
48
+
49
+ headers[0].each do |k, v|
50
+ request[k] = v
51
+ end
52
+ request['X-HMAC-Algorithm'] = algorithm
53
+
54
+ return request
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Cyclid
16
+ # Cyclid client methods
17
+ module Client
18
+ # Client API HTTP methods
19
+ module Api
20
+ # Only works for non-authenticated request (E.g. healthchecks)
21
+ class None < Base
22
+ # Do nothing
23
+ def authenticate_request(request, _uri)
24
+ request
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ # Copyright 2016 Liqwyd Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Cyclid
16
+ # Cyclid client methods
17
+ module Client
18
+ # Client API HTTP methods
19
+ module Api
20
+ # JWT token based HTTP methods
21
+ class Token < Base
22
+ # Add the token to the request
23
+ def authenticate_request(request, _uri)
24
+ request.add_field('Authorization', "Token #{@config.username}:#{@config.token}")
25
+ return request
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end