oauth_doorman 0.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.
@@ -0,0 +1,120 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/hash/keys'
5
+ require 'active_support/core_ext/hash/indifferent_access'
6
+ require 'active_support/core_ext/object/to_query'
7
+ require 'active_support/core_ext/object/to_param'
8
+ require 'json'
9
+ require 'httpclient'
10
+
11
+ require 'rubygems'
12
+
13
+ require "oauth_doorman/oauth_sender"
14
+ require "oauth_doorman/oauth_error"
15
+ require "oauth_doorman/oauth_access_api"
16
+ require "oauth_doorman/oauth_user_info_api"
17
+ require "oauth_doorman/oauth_domain_groups_api"
18
+
19
+ module OauthDoorman
20
+ class Api
21
+ include OauthSender
22
+ include AccessAPI
23
+ include UserInfoAPI
24
+ include DomainGroupsAPI
25
+
26
+ attr_accessor :access_token, :refresh_token, :start_time, :expires_in
27
+
28
+ DEFAULT_CONFIG =
29
+ {
30
+ :redirect_uri => "http://localhost:3000/oauth2callback",
31
+ :client_id => "393121346607.apps.googleusercontent.com",
32
+ :client_secret => "O1biKD-F1IX9h5t8LNQUFQk7",
33
+ :scopes => ["https://www.googleapis.com/auth/userinfo.email", "https://apps-apis.google.com/a/feeds/groups/"],
34
+ :auth_url => "https://accounts.google.com/o/oauth2/auth",
35
+ :token_url => "https://accounts.google.com/o/oauth2/token",
36
+ :response_type => "code",
37
+ :state => "ATAXO",
38
+ :user_info_url => "https://www.googleapis.com/oauth2/v1/userinfo",
39
+ :groups_info_url => "https://apps-apis.google.com/a/feeds/group/2.0/%s/?member=%s",
40
+ :groups_info_auth_header_name => "Authorization",
41
+ :groups_info_auth_header_content => "OAuth %s",
42
+ :groups_info_request_timeout => 5,
43
+ }
44
+
45
+ def config= conf
46
+ @config = DEFAULT_CONFIG.merge(conf)
47
+ end
48
+
49
+ def config
50
+ return @config ||= DEFAULT_CONFIG
51
+ end
52
+
53
+ def access_token
54
+ if(!@access_token)
55
+ raise "call init_connection() in your callback function first before using API methods"
56
+ end
57
+
58
+ if(access_token_has_expired)
59
+ if(@refresh_token)
60
+ @access_token = get_access_token_from_refresh_token(@refresh_token)
61
+ return @access_token
62
+ else
63
+ raise "No refresh_token to refresh access_token"
64
+ end
65
+ end
66
+
67
+ return @access_token
68
+ end
69
+
70
+ def initialize(custom_config = nil)
71
+ if(custom_config)
72
+ self.config = custom_config
73
+ end
74
+ end
75
+
76
+ def init_connection_by_code(code)
77
+ init_connection(code, nil)
78
+ end
79
+
80
+ def init_connection_by_refresh_token(refresh_token)
81
+ init_connection(nil, refresh_token)
82
+ end
83
+
84
+ def remaining_seconds_to_expiration()
85
+ return @expires_in - (Time.now - @start_time)
86
+ end
87
+
88
+ private
89
+ def init_connection(code, refresh_token)
90
+ result_hash = nil
91
+
92
+ if(code)
93
+ result_hash = get_access_or_request_token(code);
94
+
95
+ if(result_hash.keys.include?("refresh_token"))
96
+ @refresh_token = result_hash["refresh_token"]
97
+ end
98
+ end
99
+
100
+ if(refresh_token)
101
+ @refresh_token = refresh_token
102
+
103
+ result_hash = get_access_token_from_refresh_token(@refresh_token)
104
+ end
105
+
106
+ @access_token = result_hash["access_token"]
107
+ @expires_in = result_hash["expires_in"]
108
+
109
+ @start_time = Time.now()
110
+ end
111
+
112
+ def access_token_has_expired()
113
+ if((Time.now - @start_time) > (@expires_in - 10))
114
+ return true
115
+ end
116
+
117
+ return false
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module OauthDoorman
4
+ module AccessAPI
5
+ include Error
6
+
7
+ #considering force_refresh_token flag of OauthSender redirection (both or access_token only)
8
+ def get_access_or_request_token(code)
9
+ return call_grant_request_token(code, nil)
10
+ end
11
+
12
+ def get_access_token_from_refresh_token(refresh_token)
13
+ return call_grant_request_token(nil, refresh_token)
14
+ end
15
+
16
+ private
17
+ #returns
18
+ #{
19
+ # "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
20
+ # "expires_in":3920,
21
+ # "token_type":"Bearer"
22
+ #} if ["authorization_code"] = "refresh_token"
23
+ # or
24
+ #{
25
+ # "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
26
+ # "expires_in":3920,
27
+ # "token_type":"Bearer",
28
+ # "refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
29
+ #} if ["authorization_code"] = "authorization_code"
30
+ def call_grant_request_token(code, refresh_token)
31
+ hash_params = {}
32
+ hash_params["client_id"] = config[:client_id]
33
+ hash_params["client_secret"] = config[:client_secret]
34
+ if(refresh_token)
35
+ hash_params["grant_type"] = "refresh_token"
36
+ hash_params["refresh_token"] = refresh_token
37
+ else
38
+ hash_params["code"] = code
39
+ hash_params["grant_type"] = "authorization_code"
40
+ hash_params["redirect_uri"] = config[:redirect_uri]
41
+ end
42
+
43
+ result = nil
44
+
45
+ begin
46
+ hash_params.each_key { |key|
47
+ hash_params[key] = URI.escape(hash_params[key])
48
+ }
49
+
50
+ http = HTTPClient.new
51
+ result = http.post(config[:token_url], hash_params).body
52
+
53
+ process_error(result)
54
+ rescue Exception => exception
55
+ raise exception
56
+ end
57
+
58
+ return JSON.parse(result)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "nokogiri"
4
+
5
+ module OauthDoorman
6
+ module DomainGroupsAPI
7
+ include Error
8
+
9
+ def get_user_groups(domain, current_user)
10
+ result = nil
11
+
12
+ begin
13
+ url = config[:groups_info_url] % [domain, current_user]
14
+
15
+ http = HTTPClient.new
16
+ result = http.get(url, :header => {config[:groups_info_auth_header_name] => config[:groups_info_auth_header_content] % [access_token]}).body
17
+
18
+ process_error(result)
19
+ rescue Exception => exception
20
+ raise exception
21
+ end
22
+
23
+ user_groups = get_user_group_ids_from_xml(result)
24
+ return user_groups
25
+ end
26
+
27
+ def get_user_group_ids_from_xml(groups_xml)
28
+ return Nokogiri::XML(groups_xml).xpath('//apps:property[@name="groupId"]').map { |x| x['value'] }
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class OauthError < Exception; end
3
+
4
+ module OauthDoorman
5
+ module Error
6
+
7
+ def process_error(request_result)
8
+ if request_result == nil
9
+ raise OauthError, "nil response content"
10
+ end
11
+
12
+ json = JSON.parse(request_result) rescue nil
13
+
14
+ if json && json.has_key?("error")
15
+ raise OauthError, json
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module OauthDoorman
4
+ module OauthSender
5
+ #force_refresh_token flag for refresh_token enforcement
6
+ def compose_authentification_request_url(force_refresh_token)
7
+ hash_params = {}
8
+ hash_params["client_id"] = config[:client_id]
9
+ hash_params["response_type"] = config[:response_type]
10
+ hash_params["redirect_uri"] = config[:redirect_uri]
11
+ hash_params["state"] = config[:state]
12
+ hash_params["scope"] = config[:scopes].join(" ")
13
+ if(force_refresh_token)
14
+ hash_params["access_type"] = "offline"
15
+ hash_params["approval_prompt"] = "force"
16
+ else
17
+ hash_params["approval_prompt"] = "auto"
18
+ end
19
+
20
+ return "#{config[:auth_url]}?#{hash_params.to_query}"
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,30 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module OauthDoorman
4
+ module UserInfoAPI
5
+ include Error
6
+
7
+ def get_user_email()
8
+ result_hash = get_user_info()
9
+
10
+ return result_hash["email"]
11
+ end
12
+
13
+ def get_user_info()
14
+ result = nil
15
+
16
+ begin
17
+ url = "#{config[:user_info_url]}?access_token=#{access_token}"
18
+
19
+ http = HTTPClient.new
20
+ result = http.get(url).body
21
+
22
+ process_error(result)
23
+ rescue Exception => exception
24
+ raise exception
25
+ end
26
+
27
+ return JSON.parse(result)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,76 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'sinatra/base'
4
+ require 'digest/sha1'
5
+
6
+ class OauthAuthorizationInvalid < Exception ; end
7
+ class OauthAuthorizationError < Exception ; end
8
+
9
+ module Sinatra
10
+ module DoormanAuth
11
+
12
+ module Helpers
13
+
14
+ def protected
15
+ redirect doorman_sign_in_url unless session[:email] && authorize_user(session[:email])
16
+ end
17
+
18
+ def doorman_sign_in_url
19
+ doorman.compose_authentification_request_url(false)
20
+ end
21
+
22
+ def doorman
23
+ OauthDoorman::Api.new(
24
+ redirect_uri: oauth_callback_url,
25
+ client_id: settings.doorman_client_id,
26
+ client_secret: settings.doorman_client_secret,
27
+ state: "Overseer"
28
+ )
29
+ end
30
+
31
+ def oauth_callback_url
32
+ "http://#{request.env["HTTP_HOST"]}/oauth2callback"
33
+ end
34
+
35
+ def authorize_user email
36
+ raise NoMethodError, "Please implement into your sinatra 'authorize_user' method with one parameter (email). \n Return true if user is known."
37
+ end
38
+ end
39
+
40
+ def self.registered(app)
41
+ app.helpers DoormanAuth::Helpers
42
+
43
+ app.set :sessions, true
44
+ app.set :session_secret, Digest::SHA1.hexdigest("#{self.class} #{ENV['RACK_ENV']}")
45
+
46
+ #Set url to show after logout
47
+ app.set :default_url_after_sign_out, "/"
48
+ #Set url to show after login
49
+ app.set :default_url_after_sign_in, "/"
50
+
51
+ # oauth settings
52
+ app.set :doorman_app_name, "#{self.class}"
53
+ app.set :doorman_client_id, "FILL"
54
+ app.set :doorman_client_secret, "FILL"
55
+
56
+ app.get "/oauth2callback" do
57
+ door = doorman
58
+ door.init_connection_by_code(request.params["code"])
59
+ user_email = door.get_user_email
60
+ if authorize_user user_email
61
+ session[:email] = user_email
62
+ redirect settings.default_url_after_sign_in
63
+ else
64
+ raise OauthAuthorizationInvalid, "Sorry but your email: #{user_email} is not valid for authorization into #{settings.doorman_app_name}."
65
+ end
66
+
67
+ raise OauthAuthorizationError, "Sorry but your attemp to atuhorizate was not succesfull, try again..."
68
+ end
69
+
70
+ app.get "/sign_out" do
71
+ session[:email] = nil
72
+ redirect settings.default_url_after_sign_out
73
+ end
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,250 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oauth_doorman
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - jan pospisil
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: httpclient
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.2.5
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.2.5
46
+ - !ruby/object:Gem::Dependency
47
+ name: json_pure
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activesupport
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '3.0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '3.0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: nokogiri
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: shoulda
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 3.1.1
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 3.1.1
110
+ - !ruby/object:Gem::Dependency
111
+ name: shoulda-context
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: turn
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 0.9.6
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 0.9.6
142
+ - !ruby/object:Gem::Dependency
143
+ name: rack-test
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: webmock
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: shotgun
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: sinatra
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ description: composes authentification url, gets google acces_token of user account
207
+ and gets access to user Google API
208
+ email: jan.pospisil@ataxo.com
209
+ executables: []
210
+ extensions: []
211
+ extra_rdoc_files: []
212
+ files:
213
+ - lib/oauth_doorman.rb
214
+ - lib/oauth_doorman/oauth_sender.rb
215
+ - lib/oauth_doorman/oauth_access_api.rb
216
+ - lib/oauth_doorman/oauth_error.rb
217
+ - lib/oauth_doorman/oauth_user_info_api.rb
218
+ - lib/oauth_doorman/oauth_domain_groups_api.rb
219
+ - lib/oauth_doorman/sinatra.rb
220
+ homepage: https://github.com/Ataxo/oauth_doorman
221
+ licenses: []
222
+ post_install_message:
223
+ rdoc_options:
224
+ - --title
225
+ - Rake -- Ruby Make
226
+ - --main
227
+ - README
228
+ - --line-numbers
229
+ require_paths:
230
+ - lib
231
+ - lib/oauth_doorman
232
+ required_ruby_version: !ruby/object:Gem::Requirement
233
+ none: false
234
+ requirements:
235
+ - - ! '>='
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ required_rubygems_version: !ruby/object:Gem::Requirement
239
+ none: false
240
+ requirements:
241
+ - - ! '>='
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ requirements: []
245
+ rubyforge_project:
246
+ rubygems_version: 1.8.23
247
+ signing_key:
248
+ specification_version: 3
249
+ summary: google 3rd party authentification
250
+ test_files: []