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.
- data/lib/akamai/edgegrid.rb +208 -0
- 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: []
|