Class: ChefAPI::Authentication
- Inherits:
-
Object
- Object
- ChefAPI::Authentication
- Includes:
- Logify
- Defined in:
- lib/chef-api/authentication.rb
Constant Summary collapse
- SIGN_FULL_BODY =
@todo: Enable this in the future when Mixlib::Authentication supports signing the full request body instead of just the uploaded file parameter.
false
- SIGNATURE =
'algorithm=sha1;version=1.0;'.freeze
- X_OPS_SIGN =
Headers
'X-Ops-Sign'.freeze
- X_OPS_USERID =
'X-Ops-Userid'.freeze
- X_OPS_TIMESTAMP =
'X-Ops-Timestamp'.freeze
- X_OPS_CONTENT_HASH =
'X-Ops-Content-Hash'.freeze
- X_OPS_AUTHORIZATION =
'X-Ops-Authorization'.freeze
Class Method Summary collapse
-
.from_options(options = {}) ⇒ Object
Create a new signing object from the given options.
Instance Method Summary collapse
-
#canonical_key ⇒ OpenSSL::PKey::RSA
Parse the given private key.
-
#canonical_method ⇒ String
The uppercase verb.
-
#canonical_path ⇒ String
The canonical path, with duplicate and trailing slashes removed.
-
#canonical_request ⇒ String
The canonical request, from the path, body, user, and current timestamp.
-
#canonical_timestamp ⇒ String
The iso8601 timestamp for this request.
-
#content_hash ⇒ String, IO
The canonical body.
-
#encrypted_request ⇒ String
The canonical request, encrypted by the given private key.
-
#headers ⇒ Hash
The fully-qualified headers for this authentication object of the form:.
-
#initialize(user, key, verb, path, body) ⇒ Authentication
constructor
Create a new Authentication object for signing.
-
#signature_lines ⇒ Hash
The
X-Ops-Authorization-N
headers.
Constructor Details
#initialize(user, key, verb, path, body) ⇒ Authentication
Create a new Authentication object for signing. Creating an instance will not run any validations or perform any operations (this is on purpose).
72 73 74 75 76 77 78 |
# File 'lib/chef-api/authentication.rb', line 72 def initialize(user, key, verb, path, body) @user = user @key = key @verb = verb @path = path @body = body end |
Class Method Details
.from_options(options = {}) ⇒ Object
Create a new signing object from the given options. All options are required.
42 43 44 45 46 47 48 49 50 |
# File 'lib/chef-api/authentication.rb', line 42 def ( = {}) user = .fetch(:user) key = .fetch(:key) verb = .fetch(:verb) path = .fetch(:path) body = .fetch(:body) new(user, key, verb, path, body) end |
Instance Method Details
#canonical_key ⇒ OpenSSL::PKey::RSA
Handle errors when the file cannot be read due to insufficient permissions
Parse the given private key. Users can specify the private key as:
- the path to the key on disk
- the raw string key
- an +OpenSSL::PKey::RSA object+
Any other implementations are not supported and will likely explode.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/chef-api/authentication.rb', line 148 def canonical_key return @canonical_key if @canonical_key log.info "Parsing private key..." if @key.nil? log.warn "No private key given!" raise 'No private key given!' end if @key.is_a?(OpenSSL::PKey::RSA) log.debug "Detected private key is an OpenSSL Ruby object" @canonical_key = @key elsif @key =~ /(.+)\.pem$/ || File.exists?(File.(@key)) log.debug "Detected private key is the path to a file" contents = File.read(File.(@key)) @canonical_key = OpenSSL::PKey::RSA.new(contents) else log.debug "Detected private key was the literal string key" @canonical_key = OpenSSL::PKey::RSA.new(@key) end @canonical_key end |
#canonical_method ⇒ String
The uppercase verb.
205 206 207 |
# File 'lib/chef-api/authentication.rb', line 205 def canonical_method @canonical_method ||= @verb.to_s.upcase end |
#canonical_path ⇒ String
The canonical path, with duplicate and trailing slashes removed. This value is then hashed.
183 184 185 |
# File 'lib/chef-api/authentication.rb', line 183 def canonical_path @canonical_path ||= hash(@path.squeeze('/').gsub(/(\/)+$/,'')).chomp end |
#canonical_request ⇒ String
The canonical request, from the path, body, user, and current timestamp.
214 215 216 217 218 219 220 221 222 |
# File 'lib/chef-api/authentication.rb', line 214 def canonical_request [ "Method:#{canonical_method}", "Hashed Path:#{canonical_path}", "X-Ops-Content-Hash:#{content_hash}", "X-Ops-Timestamp:#{}", "X-Ops-UserId:#{@user}", ].join("\n") end |
#canonical_timestamp ⇒ String
The iso8601 timestamp for this request. This value must be cached so it is persisted throughout this entire request.
193 194 195 |
# File 'lib/chef-api/authentication.rb', line 193 def @canonical_timestamp ||= Time.now.utc.iso8601 end |
#content_hash ⇒ String, IO
The canonical body. This could be an IO object (such as #body_stream
), an actual string (such as #body
), or just the empty string if the request’s body and stream was nil.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/chef-api/authentication.rb', line 113 def content_hash return @content_hash if @content_hash if SIGN_FULL_BODY @content_hash = hash(@body || '').chomp else if @body.is_a?(Multipart::MultiIO) filepart = @body.ios.find { |io| io.is_a?(Multipart::MultiIO) } file = filepart.ios.find { |io| !io.is_a?(StringIO) } @content_hash = hash(file).chomp else @content_hash = hash(@body || '').chomp end end @content_hash end |
#encrypted_request ⇒ String
The canonical request, encrypted by the given private key.
229 230 231 |
# File 'lib/chef-api/authentication.rb', line 229 def encrypted_request canonical_key.private_encrypt(canonical_request) end |
#headers ⇒ Hash
The fully-qualified headers for this authentication object of the form:
{
'X-Ops-Sign' => 'algorithm=sha1;version=1.1',
'X-Ops-Userid' => 'sethvargo',
'X-Ops-Timestamp' => '2014-07-07T02:17:15Z',
'X-Ops-Content-Hash' => '...',
'x-Ops-Authorization-1' => '...'
'x-Ops-Authorization-2' => '...'
'x-Ops-Authorization-3' => '...'
# ...
}
97 98 99 100 101 102 103 104 |
# File 'lib/chef-api/authentication.rb', line 97 def headers { X_OPS_SIGN => SIGNATURE, X_OPS_USERID => @user, X_OPS_TIMESTAMP => , X_OPS_CONTENT_HASH => content_hash, }.merge(signature_lines) end |
#signature_lines ⇒ Hash
The X-Ops-Authorization-N
headers. This method takes the encrypted request, splits on a newline, and creates a signed header authentication request. N begins at 1, not 0 because the original author of Mixlib::Authentication did not believe in computer science.
241 242 243 244 245 246 247 |
# File 'lib/chef-api/authentication.rb', line 241 def signature_lines signature = Base64.encode64(encrypted_request) signature.split(/\n/).each_with_index.inject({}) do |hash, (line, index)| hash["#{X_OPS_AUTHORIZATION}-#{index + 1}"] = line hash end end |