Class: ChefAPI::Authentication
- Inherits:
-
Object
- Object
- ChefAPI::Authentication
- 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).
71 72 73 74 75 76 77 |
# File 'lib/chef-api/authentication.rb', line 71 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.
41 42 43 44 45 46 47 48 49 |
# File 'lib/chef-api/authentication.rb', line 41 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.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/chef-api/authentication.rb', line 147 def canonical_key return @canonical_key if @canonical_key ChefAPI::Log.info "Parsing private key..." if @key.nil? ChefAPI::Log.warn "No private key given!" raise "No private key given!" end if @key.is_a?(OpenSSL::PKey::RSA) ChefAPI::Log.debug "Detected private key is an OpenSSL Ruby object" @canonical_key = @key elsif @key =~ /(.+)\.pem$/ || File.exist?(File.(@key)) ChefAPI::Log.debug "Detected private key is the path to a file" contents = File.read(File.(@key)) @canonical_key = OpenSSL::PKey::RSA.new(contents) else ChefAPI::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.
203 204 205 |
# File 'lib/chef-api/authentication.rb', line 203 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.
181 182 183 |
# File 'lib/chef-api/authentication.rb', line 181 def canonical_path @canonical_path ||= hash(@path.squeeze("/").gsub(%r{(/)+$}, "")).chomp end |
#canonical_request ⇒ String
The canonical request, from the path, body, user, and current timestamp.
212 213 214 215 216 217 218 219 220 |
# File 'lib/chef-api/authentication.rb', line 212 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.
191 192 193 |
# File 'lib/chef-api/authentication.rb', line 191 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.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/chef-api/authentication.rb', line 112 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.
227 228 229 |
# File 'lib/chef-api/authentication.rb', line 227 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' => '...'
# ...
}
96 97 98 99 100 101 102 103 |
# File 'lib/chef-api/authentication.rb', line 96 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.
239 240 241 242 243 244 245 |
# File 'lib/chef-api/authentication.rb', line 239 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 |