omniauth-granicus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ .rspec
4
+ /Gemfile.lock
5
+ pkg/*
6
+ .powenv
7
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'jruby-openssl', :platform => :jruby
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # OmniAuth Granicus
2
+
3
+ Granicus OAuth2 Strategies for OmniAuth 1.0.
4
+
5
+ Supports the OAuth 2.0 server-side flow.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/example/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ # https://github.com/mkdynamic/omniauth-facebook/issues/20
4
+ gem 'rack', '~> 1.3.6'
5
+
6
+ gem 'sinatra'
7
+ gem 'omniauth-facebook', :path => '../'
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ omniauth-facebook (1.2.0)
5
+ omniauth-oauth2 (~> 1.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ addressable (2.2.7)
11
+ faraday (0.7.6)
12
+ addressable (~> 2.2)
13
+ multipart-post (~> 1.1)
14
+ rack (~> 1.1)
15
+ hashie (1.2.0)
16
+ multi_json (1.1.0)
17
+ multipart-post (1.1.5)
18
+ oauth2 (0.5.2)
19
+ faraday (~> 0.7)
20
+ multi_json (~> 1.0)
21
+ omniauth (1.0.2)
22
+ hashie (~> 1.2)
23
+ rack
24
+ omniauth-oauth2 (1.0.0)
25
+ oauth2 (~> 0.5.0)
26
+ omniauth (~> 1.0)
27
+ rack (1.3.6)
28
+ rack-protection (1.2.0)
29
+ rack
30
+ sinatra (1.3.2)
31
+ rack (~> 1.3, >= 1.3.6)
32
+ rack-protection (~> 1.2)
33
+ tilt (~> 1.3, >= 1.3.3)
34
+ tilt (1.3.3)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ omniauth-facebook!
41
+ rack (~> 1.3.6)
42
+ sinatra
data/example/config.ru ADDED
@@ -0,0 +1,118 @@
1
+ require 'bundler/setup'
2
+ require 'sinatra/base'
3
+ require 'omniauth-facebook'
4
+
5
+ # https://github.com/intridea/omniauth-oauth2/pull/9
6
+ require 'timeout'
7
+
8
+ SCOPE = 'email,read_stream'
9
+
10
+ class App < Sinatra::Base
11
+ # turn off sinatra default X-Frame-Options for FB canvas
12
+ set :protection, :except => :frame_options
13
+
14
+ # server-side flow
15
+ get '/' do
16
+ # NOTE: you would just hit this endpoint directly from the browser
17
+ # in a real app. the redirect is just here to setup the root
18
+ # path in this example sinatra app.
19
+ redirect '/auth/facebook'
20
+ end
21
+
22
+ # client-side flow
23
+ get '/client-side' do
24
+ content_type 'text/html'
25
+ # NOTE: when you enable cookie below in the FB.init call
26
+ # the GET request in the FB.login callback will send
27
+ # a signed request in a cookie back the OmniAuth callback
28
+ # which will parse out the authorization code and obtain
29
+ # the access_token. This will be the exact same access_token
30
+ # returned to the client in response.authResponse.accessToken.
31
+ <<-END
32
+ <html>
33
+ <head>
34
+ <title>Client-side Flow Example</title>
35
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
36
+ </head>
37
+ <body>
38
+ <div id="fb-root"></div>
39
+
40
+ <script type="text/javascript">
41
+ window.fbAsyncInit = function() {
42
+ FB.init({
43
+ appId : '#{ENV['APP_ID']}',
44
+ status : true, // check login status
45
+ cookie : true, // enable cookies to allow the server to access the session
46
+ xfbml : true // parse XFBML
47
+ });
48
+ };
49
+
50
+ (function(d) {
51
+ var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
52
+ js = d.createElement('script'); js.id = id; js.async = true;
53
+ js.src = "//connect.facebook.net/en_US/all.js";
54
+ d.getElementsByTagName('head')[0].appendChild(js);
55
+ }(document));
56
+
57
+ $(function() {
58
+ $('a').click(function(e) {
59
+ e.preventDefault();
60
+
61
+ FB.login(function(response) {
62
+ if (response.authResponse) {
63
+ $('#connect').html('Connected! Hitting OmniAuth callback (GET /auth/facebook/callback)...');
64
+
65
+ // since we have cookies enabled, this request will allow omniauth to parse
66
+ // out the auth code from the signed request in the fbsr_XXX cookie
67
+ $.getJSON('/auth/facebook/callback', function(json) {
68
+ $('#connect').html('Connected! Callback complete.');
69
+ $('#results').html(JSON.stringify(json));
70
+ });
71
+ }
72
+ }, { scope: '#{SCOPE}' });
73
+ });
74
+ });
75
+ </script>
76
+
77
+ <p id="connect">
78
+ <a href="#">Connect to FB</a>
79
+ </p>
80
+
81
+ <p id="results" />
82
+ </body>
83
+ </html>
84
+ END
85
+ end
86
+
87
+ # auth via FB canvas and signed request param
88
+ post '/canvas/' do
89
+ # we just redirect to /auth/facebook here which will parse the
90
+ # signed_request FB sends us, asking for auth if the user has
91
+ # not already granted access, or simply moving straight to the
92
+ # callback where they have already granted access.
93
+ #
94
+ # we pass the state parameter which we detect in our callback
95
+ # to do custom rendering/redirection for the canvas app page
96
+ redirect "/auth/facebook?signed_request=#{request.params['signed_request']}&state=canvas"
97
+ end
98
+
99
+ get '/auth/:provider/callback' do
100
+ # we can do something special here is +state+ param is canvas
101
+ # (see notes abovein /canvas/ method for more details)
102
+ content_type 'application/json'
103
+ MultiJson.encode(request.env)
104
+ end
105
+
106
+ get '/auth/failure' do
107
+ content_type 'application/json'
108
+ MultiJson.encode(request.env)
109
+ end
110
+ end
111
+
112
+ use Rack::Session::Cookie
113
+
114
+ use OmniAuth::Builder do
115
+ provider :facebook, ENV['APP_ID'], ENV['APP_SECRET'], :scope => SCOPE
116
+ end
117
+
118
+ run App.new
@@ -0,0 +1,5 @@
1
+ module OmniAuth
2
+ module GranicusAdmin
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'omniauth/granicus/version'
2
+ require 'omniauth/strategies/granicus-admin'
@@ -0,0 +1,136 @@
1
+ require 'omniauth/strategies/oauth2'
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'rack/utils'
5
+
6
+ module OmniAuth
7
+ module Strategies
8
+ class GranicusAdmin < OmniAuth::Strategies::OAuth2
9
+ class NoAuthorizationCodeError < StandardError; end
10
+
11
+ DEFAULT_SCOPE = ''
12
+
13
+ option :name, 'granicus_admin'
14
+
15
+ option :client_options, {
16
+ :site => 'https://citizen.dev.granicus.com',
17
+ :token_url => '/auth/oauth/token',
18
+ :authorize_url => '/auth/oauth/authorize'
19
+ }
20
+
21
+ option :token_params, {
22
+ :parse => :query
23
+ }
24
+
25
+ option :access_token_options, {
26
+ :header_format => 'OAuth %s',
27
+ :param_name => 'access_token'
28
+ }
29
+
30
+ option :authorize_options, [:scope, :display]
31
+
32
+ uid { raw_info['id'] }
33
+
34
+ info do
35
+ prune!({
36
+ 'nickname' => raw_info['username'],
37
+ 'email' => raw_info['email'],
38
+ 'name' => raw_info['name'],
39
+ 'first_name' => raw_info['first_name'],
40
+ 'last_name' => raw_info['last_name'],
41
+ 'image' => "#{options[:secure_image_url] ? 'https' : 'http'}://graph.facebook.com/#{uid}/picture?type=square",
42
+ 'description' => raw_info['bio'],
43
+ 'urls' => {
44
+ 'Facebook' => raw_info['link'],
45
+ 'Website' => raw_info['website']
46
+ },
47
+ 'location' => (raw_info['location'] || {})['name'],
48
+ 'verified' => raw_info['verified']
49
+ })
50
+ end
51
+
52
+ credentials do
53
+ prune!({
54
+ 'expires' => access_token.expires?,
55
+ 'expires_at' => access_token.expires_at
56
+ })
57
+ end
58
+
59
+ extra do
60
+ prune!({
61
+ 'raw_info' => raw_info
62
+ })
63
+ end
64
+
65
+ def raw_info
66
+ @raw_info ||= access_token.get('/me').parsed
67
+ end
68
+
69
+ def build_access_token
70
+ with_authorization_code! { super }.tap do |token|
71
+ token.options.merge!(access_token_options)
72
+ end
73
+ end
74
+
75
+ # NOTE if we're using code from the signed request
76
+ # then FB sets the redirect_uri to '' during the authorize
77
+ # phase + it must match during the access_token phase:
78
+ # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php#L348
79
+ def callback_url
80
+ options[:callback_url] || super
81
+ end
82
+
83
+ def access_token_options
84
+ options.access_token_options.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
85
+ end
86
+
87
+ ##
88
+ # You can pass +display+, +state+ or +scope+ params to the auth request, if
89
+ # you need to set them dynamically. You can also set these options
90
+ # in the OmniAuth config :authorize_params option.
91
+ #
92
+ # /auth/granicus_admin?host=sacramento.granicus.com
93
+ #
94
+ def authorize_params
95
+ super.tap do |params|
96
+ %w[host scope].each { |v| params[v.to_sym] = request.params[v] if request.params[v] }
97
+ params[:scope] ||= DEFAULT_SCOPE
98
+ if !params[:host].nil?
99
+ options.client_options[:site] = "https://#{params[:host]}"
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ ##
107
+ # Picks the authorization code in order, from:
108
+ #
109
+ # the request 'code' param (manual callback from standard server-side flow)
110
+ #
111
+ def with_authorization_code!
112
+ if request.params.key?('code')
113
+ yield
114
+ else
115
+ raise NoAuthorizationCodeError, 'must pass a `code` parameter'
116
+ end
117
+ end
118
+
119
+ def prune!(hash)
120
+ hash.delete_if do |_, value|
121
+ prune!(value) if value.is_a?(Hash)
122
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
123
+ end
124
+ end
125
+
126
+ # def valid_signature?(secret, signature, payload, algorithm = OpenSSL::Digest::SHA256.new)
127
+ # OpenSSL::HMAC.digest(algorithm, secret, payload) == signature
128
+ # end
129
+
130
+ def base64_decode_url(value)
131
+ value += '=' * (4 - value.size.modulo(4))
132
+ Base64.decode64(value.tr('-_', '+/'))
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1 @@
1
+ require 'omniauth/granicus-admin'
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'omniauth/granicus/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'omniauth-granicus'
7
+ s.version = OmniAuth::GranicusAdmin::VERSION
8
+ s.authors = ['Javier Muniz']
9
+ s.email = ['javier@granicus.com']
10
+ s.summary = 'Granicus Admin strategy for OmniAuth'
11
+ s.homepage = 'https://github.com/granicus/omniauth-granicus-admin'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_runtime_dependency 'omniauth-oauth2', '~> 1.0.0'
19
+
20
+ s.add_development_dependency 'rspec', '~> 2.7.0'
21
+ s.add_development_dependency 'rake'
22
+ end
@@ -0,0 +1,339 @@
1
+ require 'spec_helper'
2
+ require 'omniauth-granicus-admin'
3
+ require 'openssl'
4
+ require 'base64'
5
+
6
+ describe OmniAuth::Strategies::GranicusAdmin do
7
+ before :each do
8
+ @request = double('Request')
9
+ @request.stub(:params) { {} }
10
+ @request.stub(:cookies) { {} }
11
+ @request.stub(:env) { {} }
12
+
13
+ @client_id = '123'
14
+ @client_secret = '53cr3tz'
15
+ end
16
+
17
+ subject do
18
+ args = [@client_id, @client_secret, @options].compact
19
+ OmniAuth::Strategies::GranicusAdmin.new(nil, *args).tap do |strategy|
20
+ strategy.stub(:request) { @request }
21
+ end
22
+ end
23
+
24
+ it_should_behave_like 'an oauth2 strategy'
25
+
26
+ describe '#client' do
27
+ it 'has correct Granicus site' do
28
+ subject.client.site.should eq('https://citizen.dev.granicus.com')
29
+ end
30
+
31
+ it 'has correct authorize url' do
32
+ subject.client.options[:authorize_url].should eq('/auth/oauth/authorize')
33
+ end
34
+
35
+ it 'has correct token url' do
36
+ subject.client.options[:token_url].should eq('/auth/oauth/token')
37
+ end
38
+ end
39
+
40
+ describe '#callback_url' do
41
+ it "returns the default callback url" do
42
+ url_base = 'http://auth.request.com'
43
+ @request.stub(:url) { "#{url_base}/some/page" }
44
+ subject.stub(:script_name) { '' } # as not to depend on Rack env
45
+ subject.callback_url.should eq("#{url_base}/auth/granicus_admin/callback")
46
+ end
47
+
48
+ it "returns path from callback_path option" do
49
+ @options = { :callback_path => "/auth/FB/done"}
50
+ url_base = 'http://auth.request.com'
51
+ @request.stub(:url) { "#{url_base}/page/path" }
52
+ subject.stub(:script_name) { '' } # as not to depend on Rack env
53
+ subject.callback_url.should eq("#{url_base}/auth/FB/done")
54
+ end
55
+
56
+ it "returns url from callback_url option" do
57
+ url = 'https://auth.myapp.com/auth/fb/callback'
58
+ @options = { :callback_url => url }
59
+ subject.callback_url.should eq(url)
60
+ end
61
+ end
62
+
63
+ describe '#authorize_params' do
64
+ it 'includes default scope for session access' do
65
+ subject.authorize_params.should be_a(Hash)
66
+ subject.authorize_params[:scope].should eq('')
67
+ end
68
+
69
+ it 'changes site to site defined by host param in request when present' do
70
+ @request.stub(:params) { { 'host' => 'dev.dev.granicus.com' } }
71
+ subject.authorize_params.should be_a(Hash)
72
+ subject.authorize_params[:host].should eq('dev.dev.granicus.com')
73
+ subject.client.site.should eq('https://dev.dev.granicus.com')
74
+ end
75
+
76
+ it 'overrides default scope with parameter passed from request' do
77
+ @request.stub(:params) { { 'scope' => 'email' } }
78
+ subject.authorize_params.should be_a(Hash)
79
+ subject.authorize_params[:scope].should eq('email')
80
+ end
81
+ end
82
+
83
+ describe '#token_params' do
84
+ it 'has correct parse strategy' do
85
+ subject.token_params[:parse].should eq(:query)
86
+ end
87
+ end
88
+
89
+ describe '#access_token_options' do
90
+ it 'has correct param name by default' do
91
+ subject.access_token_options[:param_name].should eq('access_token')
92
+ end
93
+
94
+ it 'has correct header format by default' do
95
+ subject.access_token_options[:header_format].should eq('OAuth %s')
96
+ end
97
+ end
98
+
99
+ describe '#uid' do
100
+ before :each do
101
+ subject.stub(:raw_info) { { 'id' => '123' } }
102
+ end
103
+
104
+ it 'returns the id from raw_info' do
105
+ subject.uid.should eq('123')
106
+ end
107
+ end
108
+
109
+ describe '#info' do
110
+ context 'when optional data is not present in raw info' do
111
+ before :each do
112
+ @raw_info ||= { 'name' => 'Fred Smith' }
113
+ subject.stub(:raw_info) { @raw_info }
114
+ end
115
+
116
+ it 'has no email key' do
117
+ subject.info.should_not have_key('email')
118
+ end
119
+
120
+ it 'has no nickname key' do
121
+ subject.info.should_not have_key('nickname')
122
+ end
123
+
124
+ it 'has no first name key' do
125
+ subject.info.should_not have_key('first_name')
126
+ end
127
+
128
+ it 'has no last name key' do
129
+ subject.info.should_not have_key('last_name')
130
+ end
131
+
132
+ it 'has no location key' do
133
+ subject.info.should_not have_key('location')
134
+ end
135
+
136
+ it 'has no description key' do
137
+ subject.info.should_not have_key('description')
138
+ end
139
+
140
+ it 'has no urls' do
141
+ subject.info.should_not have_key('urls')
142
+ end
143
+
144
+ it 'has no verified key' do
145
+ subject.info.should_not have_key('verified')
146
+ end
147
+ end
148
+
149
+ context 'when optional data is present in raw info' do
150
+ before :each do
151
+ @raw_info ||= { 'name' => 'Fred Smith' }
152
+ subject.stub(:raw_info) { @raw_info }
153
+ end
154
+
155
+ it 'returns the name' do
156
+ subject.info['name'].should eq('Fred Smith')
157
+ end
158
+
159
+ it 'returns the email' do
160
+ @raw_info['email'] = 'fred@smith.com'
161
+ subject.info['email'].should eq('fred@smith.com')
162
+ end
163
+
164
+ it 'returns the username as nickname' do
165
+ @raw_info['username'] = 'fredsmith'
166
+ subject.info['nickname'].should eq('fredsmith')
167
+ end
168
+
169
+ it 'returns the first name' do
170
+ @raw_info['first_name'] = 'Fred'
171
+ subject.info['first_name'].should eq('Fred')
172
+ end
173
+
174
+ it 'returns the last name' do
175
+ @raw_info['last_name'] = 'Smith'
176
+ subject.info['last_name'].should eq('Smith')
177
+ end
178
+
179
+ it 'returns the location name as location' do
180
+ @raw_info['location'] = { 'id' => '104022926303756', 'name' => 'Palo Alto, California' }
181
+ subject.info['location'].should eq('Palo Alto, California')
182
+ end
183
+
184
+ it 'returns bio as description' do
185
+ @raw_info['bio'] = 'I am great'
186
+ subject.info['description'].should eq('I am great')
187
+ end
188
+
189
+ it 'returns the square format granicus avatar url' do
190
+ @raw_info['id'] = '321'
191
+ subject.info['image'].should eq('http://graph.facebook.com/321/picture?type=square')
192
+ end
193
+
194
+ it 'returns the Facebook link as the Facebook url' do
195
+ @raw_info['link'] = 'http://www.facebook.com/fredsmith'
196
+ subject.info['urls'].should be_a(Hash)
197
+ subject.info['urls']['Facebook'].should eq('http://www.facebook.com/fredsmith')
198
+ end
199
+
200
+ it 'returns website url' do
201
+ @raw_info['website'] = 'https://my-wonderful-site.com'
202
+ subject.info['urls'].should be_a(Hash)
203
+ subject.info['urls']['Website'].should eq('https://my-wonderful-site.com')
204
+ end
205
+
206
+ it 'return both Facebook link and website urls' do
207
+ @raw_info['link'] = 'http://www.facebook.com/fredsmith'
208
+ @raw_info['website'] = 'https://my-wonderful-site.com'
209
+ subject.info['urls'].should be_a(Hash)
210
+ subject.info['urls']['Facebook'].should eq('http://www.facebook.com/fredsmith')
211
+ subject.info['urls']['Website'].should eq('https://my-wonderful-site.com')
212
+ end
213
+
214
+ it 'returns the positive verified status' do
215
+ @raw_info['verified'] = true
216
+ subject.info['verified'].should be_true
217
+ end
218
+
219
+ it 'returns the negative verified status' do
220
+ @raw_info['verified'] = false
221
+ subject.info['verified'].should be_false
222
+ end
223
+ end
224
+
225
+ it 'returns the secure facebook avatar url when `secure_image_url` option is specified' do
226
+ @options = { :secure_image_url => true }
227
+ raw_info = { 'name' => 'Fred Smith', 'id' => '321' }
228
+ subject.stub(:raw_info) { raw_info }
229
+ subject.info['image'].should eq('https://graph.facebook.com/321/picture?type=square')
230
+ end
231
+ end
232
+
233
+ describe '#raw_info' do
234
+ before :each do
235
+ @access_token = double('OAuth2::AccessToken')
236
+ subject.stub(:access_token) { @access_token }
237
+ end
238
+
239
+ it 'performs a GET to https://graph.facebook.com/me' do
240
+ @access_token.stub(:get) { double('OAuth2::Response').as_null_object }
241
+ @access_token.should_receive(:get).with('/me')
242
+ subject.raw_info
243
+ end
244
+
245
+ it 'returns a Hash' do
246
+ @access_token.stub(:get).with('/me') do
247
+ raw_response = double('Faraday::Response')
248
+ raw_response.stub(:body) { '{ "ohai": "thar" }' }
249
+ raw_response.stub(:status) { 200 }
250
+ raw_response.stub(:headers) { { 'Content-Type' => 'application/json' } }
251
+ OAuth2::Response.new(raw_response)
252
+ end
253
+ subject.raw_info.should be_a(Hash)
254
+ subject.raw_info['ohai'].should eq('thar')
255
+ end
256
+ end
257
+
258
+ describe '#credentials' do
259
+ before :each do
260
+ @access_token = double('OAuth2::AccessToken')
261
+ @access_token.stub(:token)
262
+ @access_token.stub(:expires?)
263
+ @access_token.stub(:expires_at)
264
+ @access_token.stub(:refresh_token)
265
+ subject.stub(:access_token) { @access_token }
266
+ end
267
+
268
+ it 'returns a Hash' do
269
+ subject.credentials.should be_a(Hash)
270
+ end
271
+
272
+ it 'returns the token' do
273
+ @access_token.stub(:token) { '123' }
274
+ subject.credentials['token'].should eq('123')
275
+ end
276
+
277
+ it 'returns the expiry status' do
278
+ @access_token.stub(:expires?) { true }
279
+ subject.credentials['expires'].should eq(true)
280
+
281
+ @access_token.stub(:expires?) { false }
282
+ subject.credentials['expires'].should eq(false)
283
+ end
284
+
285
+ it 'returns the refresh token and expiry time when expiring' do
286
+ ten_mins_from_now = (Time.now + 600).to_i
287
+ @access_token.stub(:expires?) { true }
288
+ @access_token.stub(:refresh_token) { '321' }
289
+ @access_token.stub(:expires_at) { ten_mins_from_now }
290
+ subject.credentials['refresh_token'].should eq('321')
291
+ subject.credentials['expires_at'].should eq(ten_mins_from_now)
292
+ end
293
+
294
+ it 'does not return the refresh token when it is nil and expiring' do
295
+ @access_token.stub(:expires?) { true }
296
+ @access_token.stub(:refresh_token) { nil }
297
+ subject.credentials['refresh_token'].should be_nil
298
+ subject.credentials.should_not have_key('refresh_token')
299
+ end
300
+
301
+ it 'does not return the refresh token when not expiring' do
302
+ @access_token.stub(:expires?) { false }
303
+ @access_token.stub(:refresh_token) { 'XXX' }
304
+ subject.credentials['refresh_token'].should be_nil
305
+ subject.credentials.should_not have_key('refresh_token')
306
+ end
307
+ end
308
+
309
+ describe '#extra' do
310
+ before :each do
311
+ @raw_info = { 'name' => 'Fred Smith' }
312
+ subject.stub(:raw_info) { @raw_info }
313
+ end
314
+
315
+ it 'returns a Hash' do
316
+ subject.extra.should be_a(Hash)
317
+ end
318
+
319
+ it 'contains raw info' do
320
+ subject.extra.should eq({ 'raw_info' => @raw_info })
321
+ end
322
+ end
323
+
324
+ private
325
+
326
+ def signed_request(payload, secret)
327
+ encoded_payload = base64_encode_url(MultiJson.dump(payload))
328
+ encoded_signature = base64_encode_url(signature(encoded_payload, secret))
329
+ [encoded_signature, encoded_payload].join('.')
330
+ end
331
+
332
+ def base64_encode_url(value)
333
+ Base64.encode64(value).tr('+/', '-_').gsub(/\n/, '')
334
+ end
335
+
336
+ def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new)
337
+ OpenSSL::HMAC.digest(algorithm, secret, payload)
338
+ end
339
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ require 'rspec'
3
+ Dir[File.expand_path('../support/**/*', __FILE__)].each { |f| require f }
4
+
5
+ RSpec.configure do |config|
6
+ end
@@ -0,0 +1,37 @@
1
+ # NOTE it would be useful if this lived in omniauth-oauth2 eventually
2
+ shared_examples 'an oauth2 strategy' do
3
+ describe '#client' do
4
+ it 'should be initialized with symbolized client_options' do
5
+ @options = { :client_options => { 'authorize_url' => 'https://example.com' } }
6
+ subject.client.options[:authorize_url].should == 'https://example.com'
7
+ end
8
+ end
9
+
10
+ describe '#authorize_params' do
11
+ it 'should include any authorize params passed in the :authorize_params option' do
12
+ @options = { :authorize_params => { :foo => 'bar', :baz => 'zip' } }
13
+ subject.authorize_params['foo'].should eq('bar')
14
+ subject.authorize_params['baz'].should eq('zip')
15
+ end
16
+
17
+ it 'should include top-level options that are marked as :authorize_options' do
18
+ @options = { :authorize_options => [:scope, :foo], :scope => 'bar', :foo => 'baz' }
19
+ subject.authorize_params['scope'].should eq('bar')
20
+ subject.authorize_params['foo'].should eq('baz')
21
+ end
22
+ end
23
+
24
+ describe '#token_params' do
25
+ it 'should include any authorize params passed in the :authorize_params option' do
26
+ @options = { :token_params => { :foo => 'bar', :baz => 'zip' } }
27
+ subject.token_params['foo'].should eq('bar')
28
+ subject.token_params['baz'].should eq('zip')
29
+ end
30
+
31
+ it 'should include top-level options that are marked as :authorize_options' do
32
+ @options = { :token_options => [:scope, :foo], :scope => 'bar', :foo => 'baz' }
33
+ subject.token_params['scope'].should eq('bar')
34
+ subject.token_params['foo'].should eq('baz')
35
+ end
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-granicus
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Javier Muniz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: omniauth-oauth2
16
+ requirement: &2152710860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2152710860
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2152703180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.7.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152703180
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2152702700 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2152702700
47
+ description:
48
+ email:
49
+ - javier@granicus.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - README.md
57
+ - Rakefile
58
+ - example/Gemfile
59
+ - example/Gemfile.lock
60
+ - example/config.ru
61
+ - lib/omniauth-granicus-admin.rb
62
+ - lib/omniauth/granicus-admin.rb
63
+ - lib/omniauth/granicus/version.rb
64
+ - lib/omniauth/strategies/granicus-admin.rb
65
+ - omniauth-granicus.gemspec
66
+ - spec/omniauth/strategies/granicus_spec.rb
67
+ - spec/spec_helper.rb
68
+ - spec/support/shared_examples.rb
69
+ homepage: https://github.com/granicus/omniauth-granicus-admin
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.5
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Granicus Admin strategy for OmniAuth
93
+ test_files:
94
+ - spec/omniauth/strategies/granicus_spec.rb
95
+ - spec/spec_helper.rb
96
+ - spec/support/shared_examples.rb