akamai-edgegrid 1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []