akamai-edgegrid 1.0

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 (2) hide show
  1. data/lib/akamai/edgegrid.rb +208 -0
  2. metadata +62 -0
@@ -0,0 +1,208 @@
1
+ #
2
+ # = akamai/edgegrid.rb
3
+ #
4
+ # Original Author: Jonathan Landis <jlandis@akamai.com>
5
+ #
6
+ # For more information visit https://developer.akamai.com
7
+ #
8
+ # == License
9
+ #
10
+ # Copyright 2014 Akamai Technologies, Inc. All rights reserved.
11
+ #
12
+ # Licensed under the Apache License, Version 2.0 (the "License");
13
+ # you may not use this file except in compliance with the License.
14
+ # You may obtain a copy of the License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the License is distributed on an "AS IS" BASIS,
20
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ # See the License for the specific language governing permissions and
22
+ # limitations under the License.
23
+
24
+ require 'openssl'
25
+ require 'base64'
26
+ require 'logger'
27
+ require 'securerandom'
28
+ require 'uri'
29
+ require 'net/http'
30
+
31
+ module Akamai #:nodoc:
32
+ module Edgegrid #:nodoc:
33
+
34
+ # == Akamai::Edgegrid::HTTP {OPEN} client
35
+ #
36
+ # Akamai::Edgegrid::HTTP provides a subclass of Net::HTTP that adds EdgeGrid
37
+ # authentication support as specified at
38
+ # https://developer.akamai.com/stuff/Getting_Started_with_OPEN_APIs/Client_Auth.html
39
+ #
40
+ # == Example:
41
+ # >> require 'akamai/edgegrid'
42
+ # >> require 'net/http'
43
+ # >> require 'uri'
44
+ # >> require 'json'
45
+ #
46
+ # >> baseuri = URI('https://akaa-xxxxxxxxxxxx.luna.akamaiapis.net/')
47
+ # >> http = Akamai::Edgegrid::HTTP.new(address, port)
48
+ # >> http.setup_edgegrid(
49
+ # :client_token => 'akab-ccccccccc',
50
+ # :client_secret => 'ssssssssssssssssss',
51
+ # :access_token => 'akab-aaaaaaaaaaaaaa'
52
+ # )
53
+ # >> request = Net::HTTP::Get.new URI.join(
54
+ # baseuri.to_s, '/diagnostic-tools/v1/locations'
55
+ # ).to_s
56
+ # >> response = http.request(request)
57
+ # >> puts JSON.parse(response.body)['locations'][0]
58
+ # => "Hongkong, Hong Kong
59
+ #
60
+ class HTTP < Net::HTTP
61
+
62
+ private
63
+
64
+ def self.base64_hmac_sha256(data, key)
65
+ return Base64.encode64(
66
+ OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, key, data)
67
+ ).strip()
68
+ end
69
+
70
+ def self.base64_sha256(data)
71
+ return Base64.encode64(
72
+ OpenSSL::Digest::SHA256.new.digest(data)
73
+ ).strip()
74
+ end
75
+
76
+ public
77
+
78
+ # Creates a new Akamai::Edgegrid::HTTP object (takes same options as Net::HTTP)
79
+ def initialize(address, port)
80
+ super(address, port)
81
+ @use_ssl = true
82
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
83
+ end
84
+
85
+ # Creates a signing key based on the secret and timestamp
86
+ def make_signing_key(timestamp)
87
+ signing_key = self.class.base64_hmac_sha256(timestamp, @client_secret)
88
+ @log.debug("signing key: #{signing_key}")
89
+ return signing_key
90
+ end
91
+
92
+ # Returns the @headers_to_sign in normalized form
93
+ def canonicalize_headers(request)
94
+ return @headers_to_sign.select { |header|
95
+ request.key?(header)
96
+ }.map { |header|
97
+ "#{header.downcase}:#{request[header].strip.gsub(%r{\s+}, ' ')}"
98
+ }.join("\t")
99
+ end
100
+
101
+ # Returns a hash of the HTTP POST body
102
+ def make_content_hash(request)
103
+ if request.method == 'POST' and request.body_exist? and request.body.length > 0
104
+ if request.body.length > @max_body
105
+ raise "body is too large. max_body=#{@max_body}"
106
+ end
107
+
108
+ return self.class.base64_sha256(request.body)
109
+ end
110
+ return ""
111
+ end
112
+
113
+ # Returns a string with all data that will be signed
114
+ def make_data_to_sign(request, auth_header)
115
+ url = URI(request.path)
116
+ data_to_sign = [
117
+ request.method,
118
+ url.scheme,
119
+ url.host,
120
+ url.request_uri,
121
+ canonicalize_headers(request),
122
+ make_content_hash(request),
123
+ auth_header
124
+ ].join("\t")
125
+
126
+ @log.debug("data to sign: #{data_to_sign.gsub("\t", '\\t')}")
127
+ return data_to_sign
128
+ end
129
+
130
+ # Returns a signature of the given request, timestamp and auth_header
131
+ def sign_request(request, timestamp, auth_header)
132
+ return self.class.base64_hmac_sha256(
133
+ make_data_to_sign(request, auth_header),
134
+ make_signing_key(timestamp)
135
+ )
136
+ end
137
+
138
+ alias_method :orig_request, :request
139
+
140
+ # returns the current time in the format understood by Edgegrid
141
+ def self.eg_timestamp()
142
+ return Time.now.utc.strftime('%Y%m%dT%H:%M:%S+0000')
143
+ end
144
+
145
+ # returns a new nonce (unique identifier)
146
+ def self.new_nonce()
147
+ return SecureRandom.uuid
148
+ end
149
+
150
+ # Configures Akamai::Edgegrid::HTTP for use
151
+ #
152
+ # ==== Options
153
+ # * +:client_token+ - Client Token from "Credentials" Manage API UI
154
+ # * +:client_secret+ - Client Secret from "Credentials" Manage API UI
155
+ # * +:access_token+ - Access Token from "Authorizations" Manage API UI
156
+ # * +:headers_to_sign+ - List of headers (in order) that will be signed. This info is provided by individual APIs (default [])
157
+ # * +:max_body+ - Maximum POST body size accepted. This info is provided by individual APIs (default 2048)
158
+ # * +:debug+ - Enable extra logging (default 'false')
159
+ def setup_edgegrid(opts)
160
+ @client_token = opts[:client_token]
161
+ @client_secret = opts[:client_secret]
162
+ @access_token = opts[:access_token]
163
+
164
+ @headers_to_sign = opts[:headers_to_sign]
165
+ @headers_to_sign ||= []
166
+
167
+ @max_body = opts[:max_body]
168
+ @max_body ||= 2048
169
+
170
+ if opts[:debug]
171
+ @log = Logger.new(STDERR)
172
+ else
173
+ @log = Logger.new('/dev/null')
174
+ end
175
+ end
176
+
177
+ # Returns the computed Authorization header for the given request, timestamp and nonce
178
+ def make_auth_header(request, timestamp, nonce)
179
+ auth_header = "EG1-HMAC-SHA256 " + [
180
+ "client_token" => @client_token,
181
+ "access_token" => @access_token,
182
+ "timestamp" => timestamp,
183
+ "nonce" => nonce
184
+ ].map {|kvp|
185
+ kvp.keys.map { |k| k + "=" + kvp[k] }
186
+ }.join(';') + ';'
187
+
188
+ @log.debug("unsigned authorization header: #{auth_header}")
189
+
190
+ signed_auth_header = auth_header + 'signature=' + sign_request(
191
+ request, timestamp, auth_header
192
+ )
193
+ @log.debug("signed authorization header: #{signed_auth_header}")
194
+
195
+ return signed_auth_header
196
+ end
197
+
198
+ # Same as Net::HTTP.request but with 'Authorization' header for {OPEN} Edgegrid added
199
+ # to the given request
200
+ def request(req, body=nil, &block)
201
+ timestamp = self.class.eg_timestamp()
202
+ nonce = self.class.new_nonce()
203
+ req['Authorization'] = make_auth_header(req, timestamp, nonce)
204
+ return orig_request(req, body, &block)
205
+ end
206
+ end
207
+ end
208
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: akamai-edgegrid
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jonathan Landis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: simplecov
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.7.1
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.7.1
30
+ description: Implements the Akamai {OPEN} EdgeGrid Authentication specified by https://developer.akamai.com/stuff/Getting_Started_with_OPEN_APIs/Client_Auth.html
31
+ email: jlandis@akamai.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - lib/akamai/edgegrid.rb
37
+ homepage: https://github.com/akamai-open/AkamaiOPEN-edgegrid-ruby
38
+ licenses:
39
+ - Apache
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '1.9'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.23.2
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Akamai {OPEN} EdgeGrid Authenticator for net/http
62
+ test_files: []