fidor_starter_kits 0.2.3 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a82c8968c4e81c82599966dd950785cb798a5285
4
- data.tar.gz: c8686d4dafd47ced1aebceb895a8bd4cee210ecb
3
+ metadata.gz: c225915b009fa0fb2bc2e50eb38d390bc6a9a0b7
4
+ data.tar.gz: 3547304f05e844378c9980252dd3ddf40717f4f2
5
5
  SHA512:
6
- metadata.gz: 329d6c28ef1c0acd2f1465fdb1cc9738c6a0fd56a2fe88726515f5d1c24ef685967edf79c68756ce18f77b51555d77a7f79cbcd945fcf9d6092e70045b1df205
7
- data.tar.gz: b56cfeabdd6c75febe3b7fe387a5de57bd6c74bab7ccff50f7bfb6a67bd8dfefbd072b7a6b48d5e5892096942f75331187a2cef0251b132c62ca796f5f1629d2
6
+ metadata.gz: b6fa01c0bdf645fe69e7bfab33b2ee7ac2a7ede087f7ef71dbefef88efeb80fb674554b40cf10f80b4d9dc9b808434c5372e7059c6d8ca52cf603e19e1e0dbe7
7
+ data.tar.gz: 21888227a82c338ad77845ae9c1d0cce1eb84a71202c0907eed5bcb8f351fbe5eefb6e00221144f87455c2030a87e4172bc2efd6623c0415956269bb5bb4c266
data/.gitignore CHANGED
@@ -22,3 +22,4 @@ secrets.yml
22
22
  .ruby-gemset
23
23
  *.swp
24
24
  node_modules
25
+ .DS_Store
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog Fidor Admin API Schema
2
+ See [commit messages](https://github.com/fidor/fidor_starter_kits/commits/) for details.
3
+
4
+
5
+ ##2014-12
6
+
7
+ * make starter kits use all required oauth params: state, response_type, grant_type
8
+
9
+
10
+ ##2014-11
11
+
12
+ * init with plain oauth examples for php, ruby, nodejs, golang, java
data/README.md CHANGED
@@ -1,12 +1,18 @@
1
1
  # FidorStarterKits - BETA
2
2
 
3
- This repo features fidor application examples. The apps use oAuth to
4
- authenticate with fidor, so you must register an app at fidor first to get your
5
- personal app credentials.
6
- Afterwards just copy one of the /starter_kits, add your app credentials to the
7
- example source and start to play.
3
+ This repo features Fidor application examples. The apps use oAuth to
4
+ authenticate with fidor, so you must register an app at fidor first to get your
5
+ personal app credentials.
8
6
 
9
- For Ruby Heros, this repo is also available as ruby gem and provides a tiny
7
+ For a quick start the Fidor App Manager(where you register your app) features a
8
+ 'create&download app' method. This creates a new app, writes the required oauth
9
+ information into the source files and delivers the example as zipped download.
10
+
11
+ If you choose the manual way to explore our example apps, simply checkout this
12
+ repo, register and app and add the required credentials, URL's to the sources.
13
+
14
+
15
+ For Ruby Heros, this repo is also available as ruby gem and provides a tiny
10
16
  helper for app creation, see Install & Usage.
11
17
 
12
18
 
@@ -38,7 +44,7 @@ Create a zipped app with credentials and the fidor url:
38
44
  fidor_oauth_url: 'https://fidor-oauth-url.de/oauth',
39
45
  fidor_api_url: 'https://fidor-api-url.de/api_sandbox'
40
46
  }
41
-
47
+
42
48
  zip_file_path = FidorStarterKits.build(opts)
43
49
  # => /tmp/sinatra_plain-xyz/sinatra_plain.zip
44
50
  # => mv / cp / download is up to you babee
@@ -46,9 +52,10 @@ Create a zipped app with credentials and the fidor url:
46
52
  ```
47
53
  ## Build your own starter kit
48
54
 
49
- As a quickstart for new developers we zip and download the examples in our
50
- application manager. Before the following placeholders inside in your main
51
- example.xy file are substituted with the according values from the app:
55
+ As a quickstart for new developers we zip and download the examples in our
56
+ application manager. Before the following placeholders inside in your main
57
+ example.xy file are substituted with the according values from the app
58
+ (client_id/secret) and the values in .fidor_meta.json:
52
59
 
53
60
  <APP_URL> # default http://localhost:8000/example.php
54
61
  <CLIENT_ID>
@@ -56,7 +63,7 @@ example.xy file are substituted with the according values from the app:
56
63
  <FIDOR_OAUTH_URL> # e.g https://fidor.com/oauth
57
64
  <FIDOR_API_URL>
58
65
 
59
- So just add those to example.[rb, php, ..] and see existing examples and specs
66
+ So just add those to example.[rb, php, ..] and see existing examples and specs
60
67
  for a reference.
61
68
 
62
69
  ## Contributing
@@ -1,3 +1,3 @@
1
1
  module FidorStarterKits
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -16,6 +16,7 @@ import (
16
16
  "fmt"
17
17
  "net/http"
18
18
  "net/url"
19
+ "errors"
19
20
  )
20
21
 
21
22
  // The following sections define the settings you require for the
@@ -23,17 +24,17 @@ import (
23
24
 
24
25
  // app ID and secret, can be found in this apps "Details" page in the
25
26
  // AppManager.
26
- var client_id = "<CLIENT_ID>"
27
+ var client_id = "<CLIENT_ID>"
27
28
  var client_secret = "<CLIENT_SECRET>"
28
29
 
29
30
  // Fidor's OAuth Endpoint (this changes between Sandbox and Production)
30
31
  var fidor_oauth_url = "<FIDOR_OAUTH_URL>" // e.g https://fidor.com/api_sandbox/oauth
31
32
  // The OAuth Endpoint this App provides
32
- var oauth_cb_url = "<APP_URL>"
33
+ var oauth_cb_url = "<APP_URL>"
33
34
 
34
35
  // The URL of the Fidor API (this changes between Sandbox and
35
36
  // Production)
36
- var fidor_api_url = "<FIDOR_API_URL>" // e.g https://fidor.com/api_sandbox vs /api
37
+ var fidor_api_url = "<FIDOR_API_URL>" // e.g https://fidor.com/api_sandbox vs /api
37
38
 
38
39
 
39
40
 
@@ -72,7 +73,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
72
73
  } else {
73
74
  // we don't have an oauth `code` yet, so we need to
74
75
  // redirect the user to the OAuth provider to get one ...
75
- oauth_url := fmt.Sprintf("%s/authorize?client_id=%s&redirect_uri=%s",
76
+ oauth_url := fmt.Sprintf("%s/authorize?client_id=%s&state=123&response_type=code&redirect_uri=%s",
76
77
  fidor_oauth_url,
77
78
  client_id,
78
79
  url.QueryEscape(oauth_cb_url))
@@ -99,11 +100,17 @@ func retrieveTokenFromCode(code string) (token string, err error) {
99
100
  "client_id": {client_id},
100
101
  "client_secret": {client_secret},
101
102
  "code": {code},
103
+ "redirect_uri": {url.QueryEscape(oauth_cb_url)},
104
+ "grant_type": {"autorization_code"}
102
105
  }
103
106
  // Call API
104
107
  if resp, err := http.PostForm(tokenUrl, tokenPayload); err != nil {
108
+ println(err)
105
109
  return "", err
106
110
  } else {
111
+ if resp.StatusCode != 200 {
112
+ return "", errors.New(resp.Status)
113
+ }
107
114
  // if successful, pick the access_token out of the reply.
108
115
  var tokenResponse TokenResponse
109
116
  decoder := json.NewDecoder(resp.Body)
@@ -85,6 +85,7 @@ public class Example extends HttpServlet {
85
85
  .addParameter("redirect_uri", app_url)
86
86
  .addParameter("code", code)
87
87
  .addParameter("client_secret", client_secret)
88
+ .addParameter("grant_type", "authorization_token")
88
89
  .build();
89
90
 
90
91
  HttpResponse resp = httpClient.execute(req);
@@ -103,7 +104,7 @@ public class Example extends HttpServlet {
103
104
  }
104
105
 
105
106
  private String getCodeUrl() {
106
- return fidor_oauth_url + "/authorize?client_id=" + client_id + "&redirect_uri=" + app_url;
107
+ return fidor_oauth_url + "/authorize?client_id=" + client_id + "&redirect_uri=" + app_url +"&state=1234&response_type=authroization_code";
107
108
  }
108
109
 
109
110
  private String getAccountUrl(String token) {
@@ -14,119 +14,36 @@
14
14
  // manager an need to be transfered into the config below:
15
15
 
16
16
  var fidor_config = {
17
- app_port : 3141, // you might want to change this to match your app_url
18
- app_url : "<APP_URL>", // must also include the port e.g http://localhost:3141
17
+ app_port : 3001,
18
+ app_url : "<APP_URL>",
19
19
  client_id : "<CLIENT_ID>",
20
20
  client_secret : "<CLIENT_SECRET>",
21
21
  fidor_api_url : "<FIDOR_API_URL>"
22
22
  }
23
23
 
24
- // This app can be started by executing:
25
- //
26
- // node example.js
27
- //
28
- // Once the app is running, it can be accessed on:
29
- //
30
- // http://localhost:3141
31
- //
32
- // or on whatever port is configured above.
33
- //
34
- // The app checks whether an OAuth access token is available for the
35
- // user, if not, the user is redirected to the OAuth endpoint. After
36
- // successful authentication and authorization, the OAuth endpoint
37
- // redirects the user back to the app. Specifically to:
38
- //
39
- // http://localhost:3141/code
40
- //
41
- // This redirect will contain a query with the OAuth code. The app uses
42
- // this code to request an OAuth access-token. The retrieved token is stored with
43
- // the users session and is used to authenticate API requests.
44
-
45
- var http = require('http')
46
- var url = require('url')
47
- var querystring = require('querystring')
48
-
49
-
50
-
51
- // the actual call to the api.
52
- // @param accessToken : OAuth access_token for this user.
53
- // @param cb : callback function(err, transactions)
54
- function apiGetTransactions(accessToken, cb) {
55
- // URL endpoint to call to retrieve transactions.
56
- var tx_url = fidor_config.fidor_api_url+"/transactions?access_token="+ accessToken
57
-
58
- var get = http.get(tx_url, function(res){
59
- // response may come in numerous chunks, we need to collect
60
- // them and reassemble the entire answer when all data has
61
- // been retrieved.
62
- var data = new Buffer(0)
63
- res.on('data', function(chunk){
64
- data = Buffer.concat([data, chunk])
65
- })
66
- res.on('end', function() {
67
- if (res.statusCode !== 200) {
68
- cb(data.toString(), null)
69
- return
70
- }
71
- var d = JSON.parse(data)
72
- cb(d.error, d.data)
73
- })
74
- })
75
-
76
- get.on('error', function(e) {
77
- cb (e, null)
78
- })
79
- }
80
24
 
81
- //
82
- // The handle for the Apps /transactions endpoint.
83
- // @param request : http request object
84
- // @param response : http response
85
- //
86
- function getTransactions(request, response) {
87
- var cookie_token = getCookie(request, "oauth_token")
88
- // if we don't have a token for this user already, redirect
89
- // the user to the OAuth server
90
- if (!cookie_token) {
91
- var oauth_url = fidor_config.fidor_api_url+"/oauth/authorize?"+
92
- "client_id="+fidor_config.client_id+
93
- "&redirect_uri="+fidor_config.app_url+"/code"
94
- response.writeHead(307, {"location" : oauth_url})
95
- response.end()
96
- return
97
- }
98
-
99
- // trade in the key we stored in the cookie for the actual oauth token.
100
- var oauth_token = getToken(cookie_token)
101
-
102
- // call the api with the OAuth token:
103
- apiGetTransactions(oauth_token, function (err, transactions) {
104
- if (err) {
105
- // 500 Server Error in case of any problems
106
-
107
- console.log(">>>error")
108
- console.log(err)
109
- console.log("<<<error")
110
-
111
- response.writeHead(500, "Borked.")
112
- response.end("Borked.")
113
- return
114
- }
115
- // if everything went well, dump the received json.
116
- response.writeHead(200, {"Content-Type": "application/json; charset=utf-8"})
117
- response.write(JSON.stringify(transactions, null, " "))
118
- response.end()
119
-
120
- })
25
+ // redirect the user to the OAuth authorization endpoint with the
26
+ // following params:
27
+ // - client_id
28
+ // - state
29
+ // - response_type
30
+ // - redirect_uri
31
+ function redirect_to_oauth(response){
32
+ var redirect_uri = encodeURIComponent(fidor_config.app_url)
33
+ var oauth_url = fidor_config.fidor_api_url+
34
+ "/oauth/authorize?client_id="+fidor_config.client_id+
35
+ "&state=123&response_type=code&"+
36
+ "redirect_uri="+redirect_uri
37
+ response.writeHead(307, {"location" : oauth_url})
38
+ response.end()
39
+ return
121
40
  }
122
41
 
123
- // Trades the `code` we received from the OAuth server via client
124
- // redirect for an actual access_token. To do this, the code needs to be
125
- // send to the correct OAuth endppoint together with client_id and
126
- // client_secret.
127
- function getOAuthToken(code, cb) {
42
+ <// Execute a POST request against the OAUTH token endpoint
43
+ // in order to exchange: code, client_id, client_secret,
44
+ // rerdirect_uri and grant_type for an auth_token.
45
+ function retrieve_access_token_from_code( code, cb ) {
128
46
  var oauth_url = url.parse(fidor_config.fidor_api_url)
129
-
130
47
  // where to send the data ...
131
48
  var postOptions = {
132
49
  method: "POST",
@@ -139,7 +56,9 @@ function getOAuthToken(code, cb) {
139
56
  var postData = {
140
57
  code : code,
141
58
  client_id : fidor_config.client_id,
142
- client_secret : fidor_config.client_secret
59
+ client_secret : fidor_config.client_secret,
60
+ redirect_uri : encodeURIComponent(fidor_config.app_url),
61
+ grant_type : "authorization_code"
143
62
  }
144
63
  postData = querystring.stringify(postData)
145
64
 
@@ -153,6 +72,7 @@ function getOAuthToken(code, cb) {
153
72
 
154
73
  res.on('end', function() {
155
74
  var oauth_response = JSON.parse(data)
75
+ console.log(oauth_response)
156
76
  cb(oauth_response.error, oauth_response.access_token)
157
77
  })
158
78
  })
@@ -165,178 +85,62 @@ function getOAuthToken(code, cb) {
165
85
  token_request.end()
166
86
  }
167
87
 
168
- // handler we provide to handle our user being redirected back
169
- // to us from the OAuth server with the OAuth `code` in the
170
- // query of the url.
171
- function setOAuthToken(request, response) {
172
- var u = url.parse(request.url)
173
- var code = querystring.parse(u.query)["code"]
88
+ <
89
+ // Display a friendly message and links to the API Endpoints.
90
+ function renderWelcome(request, response, token) {
91
+ response.writeHead(200, {"Content-Type" : "text/html"})
92
+ var content = hello_template.replace(/{token}/g, token)
93
+ console.log(content)
94
+ console.log(token)
95
+ content = content.replace(/{api_uri}/g, fidor_config.fidor_api_url)
96
+ response.end(content)
97
+ }
98
+
99
+ // main http functionality
100
+ function listener (request, response) {
174
101
 
175
- // if the request does not contain a ?code=adsfasdfasdf
176
- // query, it's not valid.
177
- if (!code) {
178
- response.writeHead(400, "Bad Request")
102
+ var u = url.parse(request.url)
103
+ // reject everything but GET.
104
+ if (request.method !== "GET" || u.pathname !== "/") {
105
+ response.writeHead(403, "Forbidden")
179
106
  response.end()
180
107
  return
181
108
  }
182
-
183
- // exchange the code for a token ...
184
- getOAuthToken(code, function (err, token) {
185
- if (err) {
186
- console.log(">>>error")
187
- console.log(err)
188
- console.log("<<<error")
189
-
190
- response.writeHead(500, "Borked.")
191
- response.end("Borked.")
192
- return
193
- }
194
-
195
- // once we have the token, we store it in our app (see below). The
196
- // OAuth token is stored under a random key. We set this key in the
197
- // user's cookie in order to retrieve the access_token whenever we
198
- // may need it in the future without leaking the actual access_token
199
- // via the cookie
200
-
201
- var token_key = storeToken(token)
202
- setCookie(response, "oauth_token", token_key)
203
-
204
- // send the user back to the transactions url, this time, with a
205
- // valid access_token (via the cookie we just set)
206
- response.writeHead(307, {"location" : "/transactions"})
207
- response.end()
208
- })
209
- }
210
-
211
- // handler for all http requests to our app ...
212
- function listener(req, resp) {
213
- // reject everything but GET.
214
- if (req.method !== "GET") {
215
- resp.writeHead(403, "Forbidden")
109
+ var code = querystring.parse(u.query)["code"]
110
+ if (code) {
111
+ retrieve_access_token_from_code( code, function (err, token) {
112
+ if (err) {}
113
+ renderWelcome(request, response, token)
114
+ })
115
+ } else {
116
+ // we don't have an oauth `code` yet, so we need to
117
+ // redirect the user to the OAuth provider to get one ...
118
+ redirect_to_oauth(response)
119
+ return
216
120
  }
121
+ }
217
122
 
218
- var u = url.parse(req.url)
219
- switch (u.pathname) {
220
- case "/":
221
- console.log("start page ...")
222
- resp.writeHead(200, {"Content-Type" : "text/html"})
223
- resp.end(hello_template)
224
- break
225
- case "/transactions":
226
- console.log("transactions ...")
227
- getTransactions(req, resp)
228
- break
229
- // utility code to remove cookies in order to force OAuth.
230
- case "/code":
231
- console.log("received code redirect ...")
232
- setOAuthToken(req, resp)
233
- break
234
- case "/clear_all_cookies":
235
- console.log("clearing cookies")
236
- clearCookies(req, resp)
237
- resp.writeHead(307, {"location": "/"})
238
- resp.end()
239
- break
240
- case "/clear_cookie":
241
- console.log("clearing oauth cookie")
242
-
243
- var key = getCookie(req, "oauth_token")
244
- deleteToken(key)
245
-
246
- clearCookie(resp, "oauth_token")
247
- resp.writeHead(307, {"location": "/"})
248
- resp.end()
249
- break
250
- default:
251
- console.log("unknown: "+u.path)
252
- resp.end()
253
- }
123
+ // Execution starts here...
254
124
 
255
- }
125
+ var url = require("url")
126
+ var querystring = require("querystring")
127
+ var http = require("http")
256
128
 
257
129
  // start the server
258
130
  var server = http.createServer(listener)
259
131
  server.listen(fidor_config.app_port)
260
132
 
261
- console.log("listening on : "+fidor_config.app_port)
262
-
133
+ console.log("listening on : "+fidor_config.app_url)
263
134
 
264
135
 
265
136
  var hello_template = ""+
266
- "<DOCTYPE html>" +
267
- "<html>" +
268
- "<head></head>" +
269
- "<body>" +
270
- " <h1>Welcome to Transaction Getter!</h1>" +
271
- " <p><a href='/transactions'>Get Transactions</a></p>" +
272
- " <p><a href='/clear_cookie'>Clear Cookie</a></p>" +
273
- " <p><a href='/clear_all_cookies'>Clear All Cookies</a></p>" +
274
- "</body>" +
137
+ "<html>"+
138
+ "<head>"+
139
+ "</head>"+
140
+ "<body>"+
141
+ " <h1>Welcome!</h1>"+
142
+ " <i>retrieved <tt>access_token</tt>: {token} </i>"+
143
+ " <p><a href='{api_uri}/transactions?access_token={token}'>Transactions</a></p>"+
144
+ " <p><a href='{api_uri}/accounts?access_token={token}'>Accounts</a></p>"+
145
+ "</body>"+
275
146
  "</html>"
276
-
277
- // Code for setting and clearing cookies. Typically a framework would
278
- // handle the intricacies of cookie handling, but we opted not to
279
- // require any additional dependancies for this example so we need to
280
- // handle cookies manually...
281
-
282
- function setCookie(response, key, value) {
283
- response.setHeader("Set-Cookie", key+"="+value)
284
- }
285
-
286
- function clearCookie(response, key) {
287
- console.log("clearing: "+key)
288
- if (key.map) {
289
- key = key.map(function (k) {
290
- return k+"=delete; expires=Thu, 01 Jan 1970 00:00:00 GMT"
291
- })
292
- } else {
293
- key = key+"=delete; expires=Thu, 01 Jan 1970 00:00:00 GMT"
294
- }
295
- console.log(key)
296
- response.setHeader("Set-Cookie", key)
297
- }
298
-
299
- function clearCookies(request, response){
300
- clearCookie(response, Object.keys(getCookies(request)))
301
- }
302
-
303
-
304
- function getCookies(request) {
305
- var cookies = {}
306
- var c = request.headers["cookie"]
307
- if (c) {
308
- c.split(';').forEach(function(cookie){
309
- var c = cookie.split("=")
310
- cookies[c[0].trim()] = c[1]
311
- })
312
- }
313
- return cookies
314
- }
315
-
316
- function getCookie(request, key) {
317
- var cookies = getCookies(request)
318
- return cookies[key]
319
- }
320
-
321
- // token storage
322
-
323
-
324
- var tokens = {}
325
-
326
- function storeToken (token) {
327
- var key = ""
328
- for (var i =0 ; i!= 16; ++i) {
329
- key += Math.floor((Math.random() * 256)).toString(16)
330
- }
331
- tokens[key] = token
332
- return key
333
- }
334
- function getToken(key) {
335
- return tokens[key]
336
- }
337
-
338
- function deleteToken (key) {
339
- var tok = tokens[key]
340
- delete tokens[key]
341
- return tok
342
- }
@@ -12,7 +12,8 @@
12
12
  if(empty($code)) {
13
13
  $dialog_url = $fidor_oauth_url . "/authorize?" .
14
14
  "client_id=". $app_id .
15
- "&redirect_uri=" . urlencode($app_url);
15
+ "&redirect_uri=" . urlencode($app_url) .
16
+ "&state=1234&response_type=code";
16
17
 
17
18
  echo("<script> top.location.href='" . $dialog_url . "'</script>");
18
19
  }
@@ -22,7 +23,8 @@
22
23
  $data = array('client_id' => $app_id,
23
24
  'client_secret' => $app_secret,
24
25
  'code' => $code,
25
- 'redirect_uri' => urlencode($app_url)
26
+ 'redirect_uri' => urlencode($app_url),
27
+ 'grant_type' => 'authorization_code'
26
28
  );
27
29
  // use key 'http' even if you send the request to https://...
28
30
  $options = array(
@@ -13,7 +13,7 @@ get '/' do
13
13
 
14
14
  # 1. redirect to authorize url
15
15
  unless code = params["code"]
16
- dialog_url = "#{@fidor_oauth_url}/authorize?client_id=#{@client_id}&redirect_uri=#{CGI::escape(@app_url)}"
16
+ dialog_url = "#{@fidor_oauth_url}/authorize?client_id=#{@client_id}&redirect_uri=#{CGI::escape(@app_url)}&state=1234&response_type=code"
17
17
  redirect dialog_url
18
18
  end
19
19
 
@@ -21,9 +21,11 @@ get '/' do
21
21
  token_url = URI("#{@fidor_oauth_url}/token")
22
22
  # GET and parse access_token response json
23
23
  res = Net::HTTP.post_form(token_url, 'client_id' => @client_id,
24
- 'redirect_uri' => CGI::escape(@app_url),
25
- 'code' =>code,
26
- 'client_secret'=>@client_secret)
24
+ 'redirect_uri' => CGI::escape(@app_url),
25
+ 'code' =>code,
26
+ 'client_secret'=>@client_secret,
27
+ 'grant_type'=>'authorization_code')
28
+
27
29
  resp = ActiveSupport::JSON.decode(res.body)
28
30
 
29
31
  # GET current user
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fidor_starter_kits
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Georg Leciejewski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-01 00:00:00.000000000 Z
11
+ date: 2014-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -75,6 +75,7 @@ extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
77
  - ".gitignore"
78
+ - CHANGELOG.md
78
79
  - Gemfile
79
80
  - LICENSE.txt
80
81
  - README.md