opennebula-oca 3.8.0 → 3.9.0.beta

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.
Files changed (40) hide show
  1. data/lib/{OpenNebula/Acl.rb → opennebula/acl.rb} +0 -0
  2. data/lib/{OpenNebula/AclPool.rb → opennebula/acl_pool.rb} +1 -1
  3. data/lib/{OpenNebula.rb → opennebula/client.rb} +0 -73
  4. data/lib/{OpenNebula/Cluster.rb → opennebula/cluster.rb} +1 -1
  5. data/lib/{OpenNebula/ClusterPool.rb → opennebula/cluster_pool.rb} +1 -1
  6. data/lib/{OpenNebula/Datastore.rb → opennebula/datastore.rb} +23 -1
  7. data/lib/{OpenNebula/DatastorePool.rb → opennebula/datastore_pool.rb} +1 -1
  8. data/lib/{OpenNebula/Document.rb → opennebula/document.rb} +13 -2
  9. data/lib/{OpenNebula/DocumentJSON.rb → opennebula/document_json.rb} +0 -0
  10. data/lib/{OpenNebula/DocumentPool.rb → opennebula/document_pool.rb} +2 -2
  11. data/lib/{OpenNebula/DocumentPoolJSON.rb → opennebula/document_pool_json.rb} +0 -0
  12. data/lib/opennebula/error.rb +52 -0
  13. data/lib/{OpenNebula/Group.rb → opennebula/group.rb} +1 -1
  14. data/lib/{OpenNebula/GroupPool.rb → opennebula/group_pool.rb} +1 -1
  15. data/lib/{OpenNebula/Host.rb → opennebula/host.rb} +1 -1
  16. data/lib/{OpenNebula/HostPool.rb → opennebula/host_pool.rb} +1 -1
  17. data/lib/{OpenNebula/Image.rb → opennebula/image.rb} +20 -6
  18. data/lib/{OpenNebula/ImagePool.rb → opennebula/image_pool.rb} +1 -1
  19. data/lib/opennebula/ldap_auth.rb +99 -0
  20. data/lib/opennebula/ldap_auth_spec.rb +70 -0
  21. data/lib/opennebula/pool.rb +157 -0
  22. data/lib/{OpenNebula/Pool.rb → opennebula/pool_element.rb} +1 -138
  23. data/lib/opennebula/server_cipher_auth.rb +148 -0
  24. data/lib/opennebula/server_x509_auth.rb +104 -0
  25. data/lib/opennebula/ssh_auth.rb +139 -0
  26. data/lib/opennebula/system.rb +141 -0
  27. data/lib/{OpenNebula/Template.rb → opennebula/template.rb} +13 -2
  28. data/lib/{OpenNebula/TemplatePool.rb → opennebula/template_pool.rb} +1 -1
  29. data/lib/{OpenNebula/User.rb → opennebula/user.rb} +1 -1
  30. data/lib/{OpenNebula/UserPool.rb → opennebula/user_pool.rb} +1 -1
  31. data/lib/{OpenNebula/VirtualMachine.rb → opennebula/virtual_machine.rb} +45 -25
  32. data/lib/{OpenNebula/VirtualMachinePool.rb → opennebula/virtual_machine_pool.rb} +1 -1
  33. data/lib/{OpenNebula/VirtualNetwork.rb → opennebula/virtual_network.rb} +13 -2
  34. data/lib/{OpenNebula/VirtualNetworkPool.rb → opennebula/virtual_network_pool.rb} +1 -1
  35. data/lib/opennebula/x509_auth.rb +241 -0
  36. data/lib/{OpenNebula/XMLUtils.rb → opennebula/xml_element.rb} +12 -21
  37. data/lib/opennebula/xml_pool.rb +45 -0
  38. data/lib/opennebula/xml_utils.rb +34 -0
  39. data/lib/opennebula.rb +58 -0
  40. metadata +102 -63
@@ -0,0 +1,157 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # 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
+
17
+ require 'opennebula/xml_utils'
18
+
19
+ module OpenNebula
20
+
21
+ # The Pool class represents a generic OpenNebula Pool in XML format
22
+ # and provides the basic functionality to handle the Pool elements
23
+ class Pool < XMLPool
24
+ include Enumerable
25
+
26
+ protected
27
+ #pool:: _String_ XML name of the root element
28
+ #element:: _String_ XML name of the Pool elements
29
+ #client:: _Client_ represents a XML-RPC connection
30
+ def initialize(pool,element,client)
31
+ super(nil)
32
+
33
+ @pool_name = pool.upcase
34
+ @element_name = element.upcase
35
+
36
+ @client = client
37
+ end
38
+
39
+ # Default Factory Method for the Pools. The factory method returns an
40
+ # suitable PoolElement object. Each Pool MUST implement the
41
+ # corresponding factory method
42
+ # element_xml:: _XML_ XML element describing the pool element
43
+ # [return] a PoolElement object
44
+ def factory(element_xml)
45
+ OpenNebula::PoolElement.new(element_xml,client)
46
+ end
47
+
48
+ #######################################################################
49
+ # Common XML-RPC Methods for all the Pool Types
50
+ #######################################################################
51
+
52
+ #Gets the pool without any filter. Host, Group and User Pools
53
+ # xml_method:: _String_ the name of the XML-RPC method
54
+ def info(xml_method)
55
+ return xmlrpc_info(xml_method)
56
+ end
57
+
58
+ def info_all(xml_method, *args)
59
+ return xmlrpc_info(xml_method,INFO_ALL,-1,-1, *args)
60
+ end
61
+
62
+ def info_mine(xml_method, *args)
63
+ return xmlrpc_info(xml_method,INFO_MINE,-1,-1, *args)
64
+ end
65
+
66
+ def info_group(xml_method, *args)
67
+ return xmlrpc_info(xml_method,INFO_GROUP,-1,-1, *args)
68
+ end
69
+
70
+ def info_filter(xml_method, who, start_id, end_id, *args)
71
+ return xmlrpc_info(xml_method,who, start_id, end_id, *args)
72
+ end
73
+
74
+ # Retrieves the monitoring data for all the Objects in the pool
75
+ #
76
+ # @param [String] xml_method xml-rcp method
77
+ # @param [String] root_elem Root for each individual PoolElement
78
+ # @param [String] timestamp_elem Name of the XML element with the last
79
+ # monitorization timestamp
80
+ # @param [Array<String>] xpath_expressions Elements to retrieve.
81
+ # @param args arguemnts for the xml_method call
82
+ #
83
+ # @return [Hash<String, <Hash<String, Array<Array<int>>>>>,
84
+ # OpenNebula::Error] The first level hash uses the Object ID as keys,
85
+ # and as value a Hash with the requested xpath expressions,
86
+ # and an Array of 'timestamp, value'.
87
+ def monitoring(xml_method, root_elem, timestamp_elem, xpath_expressions,
88
+ *args)
89
+
90
+ rc = @client.call(xml_method, *args)
91
+
92
+ if ( OpenNebula.is_error?(rc) )
93
+ return rc
94
+ end
95
+
96
+ xmldoc = XMLElement.new
97
+ xmldoc.initialize_xml(rc, 'MONITORING_DATA')
98
+
99
+ hash = {}
100
+
101
+ # Get all existing Object IDs
102
+ ids = xmldoc.retrieve_elements("#{root_elem}/ID")
103
+
104
+ if ids.nil?
105
+ return hash
106
+ else
107
+ ids.uniq!
108
+ end
109
+
110
+ ids.each { |id|
111
+ hash[id] = OpenNebula.process_monitoring(
112
+ xmldoc, root_elem, timestamp_elem, id, xpath_expressions)
113
+
114
+ }
115
+
116
+ return hash
117
+ end
118
+
119
+ private
120
+ # Calls to the corresponding info method to retreive the pool
121
+ # representation in XML format
122
+ # xml_method:: _String_ the name of the XML-RPC method
123
+ # args:: _Array_ with additional arguments for the info call
124
+ # [return] nil in case of success or an Error object
125
+ def xmlrpc_info(xml_method,*args)
126
+ rc = @client.call(xml_method,*args)
127
+
128
+ if !OpenNebula.is_error?(rc)
129
+ initialize_xml(rc,@pool_name)
130
+ rc = nil
131
+ end
132
+
133
+ return rc
134
+ end
135
+
136
+ public
137
+ # Constants for info queries (include/RequestManagerPoolInfoFilter.h)
138
+ INFO_GROUP = -1
139
+ INFO_ALL = -2
140
+ INFO_MINE = -3
141
+
142
+ # Iterates over every PoolElement in the Pool and calls the block with a
143
+ # a PoolElement obtained calling the factory method
144
+ # block:: _Block_
145
+ def each(&block)
146
+ each_element(block) if @xml
147
+ end
148
+
149
+ # DO NOT USE - ONLY REXML BACKEND
150
+ def to_str
151
+ str = ""
152
+ REXML::Formatters::Pretty.new(1).write(@xml,str)
153
+
154
+ return str
155
+ end
156
+ end
157
+ end
@@ -14,146 +14,9 @@
14
14
  # limitations under the License. #
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
+ require 'opennebula/pool'
17
18
 
18
19
  module OpenNebula
19
-
20
- # The Pool class represents a generic OpenNebula Pool in XML format
21
- # and provides the basic functionality to handle the Pool elements
22
- class Pool < XMLPool
23
- include Enumerable
24
-
25
- protected
26
- #pool:: _String_ XML name of the root element
27
- #element:: _String_ XML name of the Pool elements
28
- #client:: _Client_ represents a XML-RPC connection
29
- def initialize(pool,element,client)
30
- super(nil)
31
-
32
- @pool_name = pool.upcase
33
- @element_name = element.upcase
34
-
35
- @client = client
36
- end
37
-
38
- # Default Factory Method for the Pools. The factory method returns an
39
- # suitable PoolElement object. Each Pool MUST implement the
40
- # corresponding factory method
41
- # element_xml:: _XML_ XML element describing the pool element
42
- # [return] a PoolElement object
43
- def factory(element_xml)
44
- OpenNebula::PoolElement.new(element_xml,client)
45
- end
46
-
47
- #######################################################################
48
- # Common XML-RPC Methods for all the Pool Types
49
- #######################################################################
50
-
51
- #Gets the pool without any filter. Host, Group and User Pools
52
- # xml_method:: _String_ the name of the XML-RPC method
53
- def info(xml_method)
54
- return xmlrpc_info(xml_method)
55
- end
56
-
57
- def info_all(xml_method, *args)
58
- return xmlrpc_info(xml_method,INFO_ALL,-1,-1, *args)
59
- end
60
-
61
- def info_mine(xml_method, *args)
62
- return xmlrpc_info(xml_method,INFO_MINE,-1,-1, *args)
63
- end
64
-
65
- def info_group(xml_method, *args)
66
- return xmlrpc_info(xml_method,INFO_GROUP,-1,-1, *args)
67
- end
68
-
69
- def info_filter(xml_method, who, start_id, end_id, *args)
70
- return xmlrpc_info(xml_method,who, start_id, end_id, *args)
71
- end
72
-
73
- # Retrieves the monitoring data for all the Objects in the pool
74
- #
75
- # @param [String] xml_method xml-rcp method
76
- # @param [String] root_elem Root for each individual PoolElement
77
- # @param [String] timestamp_elem Name of the XML element with the last
78
- # monitorization timestamp
79
- # @param [Array<String>] xpath_expressions Elements to retrieve.
80
- # @param args arguemnts for the xml_method call
81
- #
82
- # @return [Hash<String, <Hash<String, Array<Array<int>>>>>,
83
- # OpenNebula::Error] The first level hash uses the Object ID as keys,
84
- # and as value a Hash with the requested xpath expressions,
85
- # and an Array of 'timestamp, value'.
86
- def monitoring(xml_method, root_elem, timestamp_elem, xpath_expressions,
87
- *args)
88
-
89
- rc = @client.call(xml_method, *args)
90
-
91
- if ( OpenNebula.is_error?(rc) )
92
- return rc
93
- end
94
-
95
- xmldoc = XMLElement.new
96
- xmldoc.initialize_xml(rc, 'MONITORING_DATA')
97
-
98
- hash = {}
99
-
100
- # Get all existing Object IDs
101
- ids = xmldoc.retrieve_elements("#{root_elem}/ID")
102
-
103
- if ids.nil?
104
- return hash
105
- else
106
- ids.uniq!
107
- end
108
-
109
- ids.each { |id|
110
- hash[id] = OpenNebula.process_monitoring(
111
- xmldoc, root_elem, timestamp_elem, id, xpath_expressions)
112
-
113
- }
114
-
115
- return hash
116
- end
117
-
118
- private
119
- # Calls to the corresponding info method to retreive the pool
120
- # representation in XML format
121
- # xml_method:: _String_ the name of the XML-RPC method
122
- # args:: _Array_ with additional arguments for the info call
123
- # [return] nil in case of success or an Error object
124
- def xmlrpc_info(xml_method,*args)
125
- rc = @client.call(xml_method,*args)
126
-
127
- if !OpenNebula.is_error?(rc)
128
- initialize_xml(rc,@pool_name)
129
- rc = nil
130
- end
131
-
132
- return rc
133
- end
134
-
135
- public
136
- # Constants for info queries (include/RequestManagerPoolInfoFilter.h)
137
- INFO_GROUP = -1
138
- INFO_ALL = -2
139
- INFO_MINE = -3
140
-
141
- # Iterates over every PoolElement in the Pool and calls the block with a
142
- # a PoolElement obtained calling the factory method
143
- # block:: _Block_
144
- def each(&block)
145
- each_element(block) if @xml
146
- end
147
-
148
- # DO NOT USE - ONLY REXML BACKEND
149
- def to_str
150
- str = ""
151
- REXML::Formatters::Pretty.new(1).write(@xml,str)
152
-
153
- return str
154
- end
155
- end
156
-
157
20
  # The PoolElement Class represents a generic element of a Pool in
158
21
  # XML format
159
22
  class PoolElement < XMLElement
@@ -0,0 +1,148 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # 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
+
17
+ require 'openssl'
18
+ require 'digest/sha1'
19
+
20
+ require 'base64'
21
+ require 'fileutils'
22
+
23
+ module OpenNebula; end
24
+
25
+ # Server authentication class. This method can be used by OpenNebula services
26
+ # to let access authenticated users by other means. It is based on OpenSSL
27
+ # symmetric ciphers
28
+ class OpenNebula::ServerCipherAuth
29
+ ###########################################################################
30
+ #Constants with paths to relevant files and defaults
31
+ ###########################################################################
32
+
33
+ CIPHER = "aes-256-cbc"
34
+
35
+ ###########################################################################
36
+
37
+ def initialize(srv_user, srv_passwd)
38
+ @srv_user = srv_user
39
+ @srv_passwd = srv_passwd
40
+
41
+ if !srv_passwd.empty?
42
+ @key = Digest::SHA1.hexdigest(@srv_passwd)
43
+ else
44
+ @key = ""
45
+ end
46
+
47
+ @cipher = OpenSSL::Cipher::Cipher.new(CIPHER)
48
+ end
49
+
50
+ ###########################################################################
51
+ # Client side
52
+ ###########################################################################
53
+
54
+ # Creates a ServerCipher for client usage
55
+ def self.new_client(srv_user=nil, srv_passwd=nil)
56
+ if ( srv_user == nil || srv_passwd == nil )
57
+ begin
58
+ if ENV["ONE_CIPHER_AUTH"] and !ENV["ONE_CIPHER_AUTH"].empty?
59
+ one_auth = File.read(ENV["ONE_CIPHER_AUTH"])
60
+ else
61
+ raise "ONE_CIPHER_AUTH environment variable not set"
62
+ end
63
+
64
+ one_auth.rstrip!
65
+
66
+ rc = one_auth.match(/(.*?):(.*)/)
67
+
68
+ if rc.nil?
69
+ raise "Bad format for one_auth token (<user>:<passwd>)"
70
+ else
71
+ srv_user = rc[1]
72
+ srv_passwd = rc[2]
73
+ end
74
+ rescue => e
75
+ raise e.message
76
+ end
77
+ end
78
+
79
+ self.new(srv_user, srv_passwd)
80
+ end
81
+
82
+ # Generates a login token in the form:
83
+ # - server_user:target_user:time_expires
84
+ # The token is then encrypted with the contents of one_auth
85
+ def login_token(expire, target_user=nil)
86
+ target_user ||= @srv_user
87
+ token_txt = "#{@srv_user}:#{target_user}:#{expire}"
88
+
89
+ token = encrypt(token_txt)
90
+ token64 = Base64::encode64(token).strip.delete("\n")
91
+
92
+ return "#{@srv_user}:#{target_user}:#{token64}"
93
+ end
94
+
95
+ # Returns a valid password string to create a user using this auth driver
96
+ def password
97
+ return @srv_passwd
98
+ end
99
+
100
+ ###########################################################################
101
+ # Driver side
102
+ ###########################################################################
103
+
104
+ # Creates a ServerCipher for driver usage
105
+ def self.new_driver()
106
+ self.new("","")
107
+ end
108
+
109
+ # auth method for auth_mad
110
+ def authenticate(srv_user,srv_pass, signed_text)
111
+ begin
112
+ @key = srv_pass
113
+
114
+ s_user, t_user, expires = decrypt(signed_text).split(':')
115
+
116
+ return "User name missmatch" if s_user != srv_user
117
+
118
+ return "login token expired" if Time.now.to_i >= expires.to_i
119
+
120
+ return true
121
+ rescue => e
122
+ return e.message
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def encrypt(data)
129
+ @cipher.encrypt
130
+ @cipher.key = @key
131
+
132
+ rc = @cipher.update(data)
133
+ rc << @cipher.final
134
+
135
+ return rc
136
+ end
137
+
138
+ def decrypt(data)
139
+ @cipher.decrypt
140
+ @cipher.key = @key
141
+
142
+ rc = @cipher.update(Base64::decode64(data))
143
+ rc << @cipher.final
144
+
145
+ return rc
146
+ end
147
+ end
148
+
@@ -0,0 +1,104 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # 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
+
17
+ require 'openssl'
18
+ require 'base64'
19
+ require 'fileutils'
20
+
21
+ require 'opennebula/x509_auth'
22
+
23
+ module OpenNebula; end
24
+
25
+ # Server authentication class. This authmethod can be used by opennebula services
26
+ # to let access authenticated users by other means. It is based on x509 server
27
+ # certificates
28
+ class OpenNebula::ServerX509Auth < OpenNebula::X509Auth
29
+ ###########################################################################
30
+ #Constants with paths to relevant files and defaults
31
+ ###########################################################################
32
+
33
+ SERVER_AUTH_CONF_PATH = ETC_LOCATION + "/auth/server_x509_auth.conf"
34
+
35
+ SERVER_DEFAULTS = {
36
+ :one_cert => ETC_LOCATION + "/auth/cert.pem",
37
+ :one_key => ETC_LOCATION + "/auth/key.pem"
38
+ }
39
+
40
+ ###########################################################################
41
+
42
+ def initialize()
43
+ @options = SERVER_DEFAULTS
44
+
45
+ load_options(SERVER_AUTH_CONF_PATH)
46
+
47
+ begin
48
+ certs = [ File.read(@options[:one_cert]) ]
49
+ key = File.read(@options[:one_key])
50
+
51
+ super(:certs_pem => certs, :key_pem => key)
52
+ rescue
53
+ raise
54
+ end
55
+
56
+ if @options[:srv_user] == nil || @options[:srv_user].empty?
57
+ raise "User for x509 server not defined"
58
+ end
59
+ end
60
+
61
+ ###########################################################################
62
+ # Client side
63
+ ###########################################################################
64
+
65
+ # Creates a ServerCipher for client and driver sage
66
+ class << OpenNebula::ServerX509Auth
67
+ alias :new_client :new
68
+ alias :new_driver :new
69
+ end
70
+
71
+ # Generates a login token in the form:
72
+ # - server_user:target_user:time_expires
73
+ def login_token(expire, target_user=nil)
74
+ target_user ||= @options[:srv_user]
75
+ token_txt = "#{@options[:srv_user]}:#{target_user}:#{expire}"
76
+
77
+ token = encrypt(token_txt)
78
+ token64 = Base64::encode64(token).strip.delete("\n")
79
+
80
+ return "#{@options[:srv_user]}:#{target_user}:#{token64}"
81
+ end
82
+
83
+ ###########################################################################
84
+ # Server side
85
+ ###########################################################################
86
+
87
+ # auth method for auth_mad
88
+ def authenticate(server_user, server_pass, signed_text)
89
+ begin
90
+ s_user, t_user, expires = decrypt(signed_text).split(':')
91
+
92
+ return "Server password missmatch" if server_pass != password
93
+
94
+ return "User name missmatch" if ( s_user != server_user ||
95
+ s_user != @options[:srv_user] )
96
+
97
+ return "login token expired" if Time.now.to_i >= expires.to_i
98
+
99
+ return true
100
+ rescue => e
101
+ return e.message
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,139 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # 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
+
17
+
18
+ require 'pp'
19
+ require 'openssl'
20
+ require 'base64'
21
+ require 'fileutils'
22
+
23
+ module OpenNebula; end
24
+
25
+ # SSH key authentication class. It can be used as a driver for auth_mad
26
+ # as auth method is defined. It also holds some helper methods to be used
27
+ # by oneauth command
28
+ class OpenNebula::SshAuth
29
+ LOGIN_PATH = ENV['HOME']+'/.one/one_ssh'
30
+
31
+ # Initialize SshAuth object
32
+ #
33
+ # @param [Hash] default options for path
34
+ # @option options [String] :public_key public key for the user
35
+ # @option options [String] :private_key key private key for the user.
36
+ def initialize(options={})
37
+ @private_key = nil
38
+ @public_key = nil
39
+
40
+ if options[:private_key]
41
+ begin
42
+ @private_key = File.read(options[:private_key])
43
+ rescue Exception => e
44
+ raise "Cannot read #{options[:private_key]}"
45
+ end
46
+ end
47
+
48
+ if options[:public_key]
49
+ @public_key = options[:public_key]
50
+ elsif @private_key != nil
51
+ # Init ssh keys using private key. public key is extracted in a
52
+ # format compatible with openssl. The public key does not contain
53
+ # "---- BEGIN/END RSA PUBLIC KEY ----" and is in a single line
54
+ key = OpenSSL::PKey::RSA.new(@private_key)
55
+
56
+ @public_key = key.public_key.to_pem.split("\n")
57
+ @public_key = @public_key.reject {|l| l.match(/RSA PUBLIC KEY/) }.join('')
58
+ end
59
+
60
+ if @private_key.nil? && @public_key.nil?
61
+ raise "You have to define at least one of the keys"
62
+ end
63
+ end
64
+
65
+ # Creates the login file for ssh authentication at ~/.one/one_ssh.
66
+ # By default it is valid for 1 hour but it can be changed to any number
67
+ # of seconds with expire parameter (in seconds)
68
+ def login(user, expire=3600)
69
+ expire ||= 3600
70
+
71
+ # Init proxy file path and creates ~/.one directory if needed
72
+ proxy_dir = File.dirname(LOGIN_PATH)
73
+
74
+ begin
75
+ FileUtils.mkdir_p(proxy_dir)
76
+ rescue Errno::EEXIST
77
+ end
78
+
79
+ # Generate security token
80
+ time = Time.now.to_i + expire.to_i
81
+
82
+ secret_plain = "#{user}:#{time}"
83
+ secret_crypted = encrypt(secret_plain)
84
+
85
+ proxy = "#{user}:#{secret_crypted}"
86
+
87
+ file = File.open(LOGIN_PATH, "w")
88
+ file.write(proxy)
89
+ file.close
90
+
91
+ File.chmod(0600,LOGIN_PATH)
92
+
93
+ secret_crypted
94
+ end
95
+
96
+ # Returns a valid password string to create a user using this auth driver.
97
+ # In this case the ssh public key.
98
+ def password
99
+ @public_key
100
+ end
101
+
102
+ # Checks the proxy created with the login method
103
+ def authenticate(user, token)
104
+ begin
105
+ token_plain = decrypt(token)
106
+ _user, time = token_plain.split(':')
107
+
108
+ if user == _user
109
+ if Time.now.to_i >= time.to_i
110
+ return "ssh proxy expired, login again to renew it"
111
+ else
112
+ return true
113
+ end
114
+ else
115
+ return "invalid credentials"
116
+ end
117
+ rescue
118
+ return "error"
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ ###########################################################################
125
+ # Methods to handle ssh keys
126
+ ###########################################################################
127
+ # Encrypts data with the private key of the user and returns
128
+ # base 64 encoded output in a single line
129
+ def encrypt(data)
130
+ rsa=OpenSSL::PKey::RSA.new(@private_key)
131
+ Base64::encode64(rsa.private_encrypt(data)).gsub!(/\n/, '').strip
132
+ end
133
+
134
+ # Decrypts base 64 encoded data with pub_key (public key)
135
+ def decrypt(data)
136
+ rsa=OpenSSL::PKey::RSA.new(Base64::decode64(@public_key))
137
+ rsa.public_decrypt(Base64::decode64(data))
138
+ end
139
+ end