oauth_doorman 0.1.0

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