devise_meetup_authenticatable 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,54 @@
1
- require 'devise'
2
1
 
3
- require 'devise_meetup_authenticatable/schema'
4
- require 'devise_meetup_authenticatable/strategy'
5
- require 'devise_meetup_authenticatable/routes'
2
+ require 'devise'
3
+ require 'oauth'
4
+ require 'devise_meetup_authenticatable/model'
5
+ require 'devise_meetup_authenticatable/strategy'
6
+ require 'devise_meetup_authenticatable/schema'
7
+ require 'devise_meetup_authenticatable/routes'
8
+ require 'devise_meetup_authenticatable/view_helpers'
9
+
10
+
11
+ module Devise
12
+
13
+ mattr_accessor :meetup_uid_field
14
+ @@meetup_uid_field = :meetup_uid
15
+
16
+ # Specifies the name of the database column name used for storing
17
+ # the user Facebook session key. Useful if this info should be saved in a
18
+ # generic column if different authentication solutions are used.
19
+ mattr_accessor :meetup_token_field
20
+ @@meetup_token_field = :meetup_token
21
+
22
+ # Specifies if account should be created if no account exists for
23
+ # a specified Facebook UID or not.
24
+ mattr_accessor :meetup_auto_create_account
25
+ @@meetup_auto_create_account = true
26
+
27
+ def self.meetup_client
28
+ @@meetup_client ||= OAuth::Consumer.new("ABDAE5ED0962D3332A0B546174997828", "856263601BB15FA05D1062AA082FF6CD", :site => "http://www.meetup.com/", :request_token_url => "http://www.meetup.com/oauth/request/", :authorize_path => 'authorize/', :access_token_path => 'oauth/access/', :oauth_callback => "oob", :http_method => :post)
29
+
30
+ end
31
+
32
+
33
+ def self.session_sign_in_url(request, mapping)
34
+ url = URI.parse(request.url)
35
+ # url.path = "#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
36
+ url.path = "#{mapping.full_path}/#{mapping.path_names[:meetup]}"
37
+ url.query = nil
38
+ url.to_s
39
+ end
40
+
41
+ def self.requested_scope
42
+ @@requested_scope ||= 'meetup'
43
+ end
44
+
45
+ end
46
+
47
+
48
+
6
49
 
7
50
  Devise.add_module(:meetup_authenticatable,
8
51
  :strategy => true,
9
- :model => 'devise_meetup_authenticatable/model',
10
52
  :controller => :sessions,
11
- :route => :meetup)
12
-
53
+ :route => :meetup,
54
+ :model => 'devise_meetup_authenticatable/model')
@@ -1,16 +1,185 @@
1
- module Devise
2
- module Models
3
- module MeetupAuthenticatable
4
- def self.included(base)
5
- base.extend ClassMethods
6
- end
7
-
8
- module ClassMethods
9
- def find_by_meetup_token(meetup_token)
10
- find(:first, :conditions => {:meetup_token => meetup_token})
11
- end
12
- end
13
- end
14
- end
15
- end
1
+
2
+ module MeetupAuthenticatable
16
3
 
4
+ module Models
5
+ # Adds MeetupAuthenticatable support to your model. The whole workflow is deeply discussed in the
6
+ # README. This module adds just a class +oauth_access_token+ helper to your model
7
+ # which assists you on creating an access token. All the other OAuth hooks in
8
+ # Devise must be implemented by yourself in your application.
9
+ #
10
+ # == Options
11
+ #
12
+ # Oauthable adds the following options to devise_for:
13
+ #
14
+ # * +oauth_providers+: Which providers are avaialble to this model. It expects an array:
15
+ #
16
+ # devise_for :database_authenticatable, :meetup_authenticatable, :oauth_providers => [:twitter]
17
+ #
18
+ module MeetupAuthenticatable
19
+
20
+ def self.included(base) #:nodoc:
21
+ base.class_eval do
22
+ extend ClassMethods
23
+ end
24
+ end
25
+
26
+ # Store meetup Connect account/session credentials.
27
+ #
28
+ def store_meetup_credentials!(attributes = {})
29
+ self.send(:"#{self.class.meetup_uid_field}=", attributes[:uid])
30
+ self.send(:"#{self.class.meetup_token_field}=", attributes[:token])
31
+
32
+ # Confirm without e-mail - if confirmable module is loaded.
33
+ self.skip_confirmation! if self.respond_to?(:skip_confirmation!)
34
+
35
+ # Only populate +email+ field if it's available (e.g. if +authenticable+ module is used).
36
+ self.email = attributes[:email] || '' if self.respond_to?(:email)
37
+
38
+ # Lazy hack: These database fields are required if +authenticable+/+confirmable+
39
+ # module(s) is used. Could be avoided with :null => true for authenticatable
40
+ # migration, but keeping this to avoid unnecessary problems.
41
+ self.password_salt = '' if self.respond_to?(:password_salt)
42
+ self.encrypted_password = '' if self.respond_to?(:encrypted_password)
43
+ end
44
+
45
+ # Checks if meetup Connected.
46
+ #
47
+ def meetup_connected?
48
+ self.send(:"#{self.class.meetup_uid_field}").present?
49
+ end
50
+ alias :is_meetup_connected? :meetup_connected?
51
+
52
+ # Hook that gets called *before* connect (only at creation). Useful for
53
+ # specifiying additional user info (etc.) from meetup.
54
+ #
55
+ # Default: Do nothing.
56
+ #
57
+ # == Examples:
58
+ #
59
+ # # Overridden in meetup Connect:able model, e.g. "User".
60
+ # #
61
+ # def before_meetup_auto_create(meetup_user_attributes)
62
+
63
+ # self.profile.first_name = meetup_user_attributes.first_name
64
+
65
+ #
66
+ # end
67
+ #
68
+ # == For more info:
69
+ #
70
+ # * http://oauth2er.pjkh.com/user/populate
71
+ #
72
+ def on_before_meetup_auto_create(meetup_user_attributes)
73
+
74
+ if self.respond_to?(:before_meetup_auto_create)
75
+ self.send(:before_meetup_auto_create, meetup_user_attributes) rescue nil
76
+ end
77
+ end
78
+
79
+ # Hook that gets called *after* a connection (each time). Useful for
80
+ # fetching additional user info (etc.) from meetup.
81
+ #
82
+ # Default: Do nothing.
83
+ #
84
+ # == Example:
85
+ #
86
+ # # Overridden in meetup Connect:able model, e.g. "User".
87
+ # #
88
+ # def after_meetup_connect(meetup_user_attributes)
89
+ # # See "on_before_meetup_connect" example.
90
+ # end
91
+ #
92
+ def on_after_meetup_connect(meetup_user_attributes)
93
+
94
+ if self.respond_to?(:after_meetup_auto_create)
95
+ self.send(:after_meetup_auto_create, meetup_user_attributes) rescue nil
96
+ end
97
+ end
98
+
99
+ # Optional: Store session key.
100
+ #
101
+ def store_session(using_token)
102
+ if self.token != using_token
103
+ self.update_attribute(self.send(:"#{self.class.meetup_token_field}"), using_token)
104
+ end
105
+ end
106
+
107
+ protected
108
+
109
+ # Passwords are always required if it's a new rechord and no oauth_id exists, or if the password
110
+ # or confirmation are being set somewhere.
111
+ def password_required?
112
+
113
+ ( new_record? && meetup_uid.nil? ) || !password.nil? || !password_confirmation.nil?
114
+ end
115
+
116
+ module ClassMethods
117
+
118
+ # Configuration params accessible within +Devise.setup+ procedure (in initalizer).
119
+ #
120
+ # == Example:
121
+ #
122
+ # Devise.setup do |config|
123
+ # config.meetup_uid_field = :meetup_uid
124
+ # config.meetup_token_field = :meetup_token
125
+ # config.meetup_auto_create_account = true
126
+ # end
127
+ #
128
+ ::Devise::Models.config(self,
129
+ :meetup_uid_field,
130
+ :meetup_token_field,
131
+ :meetup_auto_create_account
132
+ )
133
+
134
+ # Alias don't work for some reason, so...a more Ruby-ish alias
135
+ # for +meetup_auto_create_account+.
136
+ #
137
+ def meetup_auto_create_account?
138
+ self.meetup_auto_create_account
139
+ end
140
+
141
+ # Authenticate a user based on meetup UID.
142
+ #
143
+ def authenticate_with_meetup(meetup_id, meetup_token)
144
+
145
+ # find user and update access token
146
+ returning(self.find_for_meetup(meetup_id)) do |user|
147
+ user.update_attributes(:meetup_token => meetup_token) unless user.nil?
148
+ end
149
+
150
+ end
151
+
152
+ protected
153
+
154
+
155
+
156
+ # Find first record based on conditions given (meetup UID).
157
+ # Overwrite to add customized conditions, create a join, or maybe use a
158
+ # namedscope to filter records while authenticating.
159
+ #
160
+ # == Example:
161
+ #
162
+ # def self.find_for_meetup(uid, conditions = {})
163
+ # conditions[:active] = true
164
+ # self.find_by_meetup_uid(uid, :conditions => conditions)
165
+ # end
166
+ #
167
+ def find_for_meetup(uid, conditions = {})
168
+
169
+ self.find_by_meetup_uid(uid, :conditions => conditions)
170
+ end
171
+
172
+
173
+
174
+ # Contains the logic used in authentication. Overwritten by other devise modules.
175
+ # In the meetup Connect case; nothing fancy required.
176
+ #
177
+ def valid_for_meetup(resource, attributes)
178
+ true
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+ end
185
+ end
@@ -1,7 +1,15 @@
1
- ActionDispatch::Routing::Mapper.class_eval do
2
-
3
- # map.after_login 'oauth_complete', :controller => 'meetup_callback', :action => 'after_login'
4
- alias_method :oauth_complete, :devise_session
1
+ ActionDispatch::Routing::Mapper.class_eval do
2
+ protected
3
+
4
+ def devise_meetup(mapping, controllers)
5
+ scope mapping.full_path do
6
+ map.meetup '/meetup',:controller=>'oauth',:action=>'index'
7
+ map.authorize '/meetup/authorize',:controller=>'oauth',:action=>'authorize'
8
+ map.request_token '/meetup/request_token',:controller=>'oauth',:action=>'request_token'
9
+ map.access_token '/meetup/access_token',:controller=>'oauth',:action=>'access_token'
10
+ map.test_request '/meetup/test_request',:controller=>'oauth',:action=>'test_request'
11
+
12
+ end
5
13
  end
14
+ end
6
15
 
7
- end
@@ -1,9 +1,22 @@
1
- Devise::Schema.class_eval do
2
- def meetup_authenticatable
3
- if respond_to?(:apply_devise_schema)
4
- apply_devise_schema :meetup_token, String
5
- else
6
- apply_schema :meetup_token, String
1
+ # encoding: utf-8
2
+ require 'devise/schema'
3
+
4
+ module Devise #:nodoc:
5
+ module MeetupAuthenticatable #:nodoc:
6
+
7
+ module Schema
8
+
9
+ # Database migration schema for meetup Connect.
10
+ #
11
+ def meetup_authenticatable
12
+ apply_devise_schema ::Devise.meetup_uid_field, Integer, :limit => 8 # BIGINT unsigned / 64-bit int
13
+ apply_devise_schema ::Devise.meetup_token_field, String, :limit => 149 # [128][1][20] chars
14
+ end
15
+
7
16
  end
8
17
  end
18
+ end
19
+
20
+ Devise::Schema.module_eval do
21
+ include ::Devise::MeetupAuthenticatable::Schema
9
22
  end
@@ -1,32 +1,103 @@
1
+ # encoding: utf-8
1
2
  require 'devise/strategies/base'
2
- require 'rack/oauth'
3
3
 
4
- module Devise
5
- module Strategies
6
- class MeetupAuthenticatable < Base
7
-
8
- include Rack::OAuth::Methods
9
-
10
-
11
- def valid?
12
- get_access_token.present?
4
+
5
+ module Devise #:nodoc:
6
+ module MeetupAuthenticatable #:nodoc:
7
+ module Strategies #:nodoc:
8
+
9
+ # Default strategy for signing in a user using Facebook Connect (a Facebook account).
10
+ # Redirects to sign_in page if it's not authenticated
11
+ #
12
+ class MeetupAuthenticatable < ::Devise::Strategies::Base
13
+
13
14
 
14
- end
15
15
 
16
- def authenticate!
17
- logger.debug("Authenticating with Meetup for mapping #{mapping.to}")
18
- session[:info] = get_access_token.get('/account/verify_credentials.json').body
19
- end
20
-
21
- private
22
-
16
+ # Without a oauth session authentication cannot proceed.
17
+ #
18
+ def valid?
19
+
20
+ valid_controller? && valid_params? && mapping.to.respond_to?('authenticate_with_meetup')
21
+
22
+ end
23
+
24
+ # Authenticate user with meetup
25
+ #
26
+ def authenticate!
27
+ klass = mapping.to
28
+ begin
29
+
30
+
31
+ # Verify User Auth code and get access token from auth server: will error on failue
32
+ access_token = Devise::meetup_client.request_token.get_access_token
33
+
34
+
35
+ # retrieve user attributes
36
+
37
+ # Get user details from meetup Service
38
+ # NOTE: Facebook Graph Specific
39
+ # TODO: break this out into separate model or class to handle
40
+ # different meetup providers
41
+ meetup_user_attributes = JSON.parse(access_token.get('/me'))
23
42
 
24
- def logger
25
- @logger ||= ((Rails && Rails.logger) || RAILS_DEFAULT_LOGGER)
43
+ user = klass.authenticate_with_meetup(meetup_user_attributes['id'], access_token.token)
44
+
45
+
46
+
47
+ if user.present?
48
+ user.on_after_meetup_connect(meetup_user_attributes)
49
+ success!(user)
50
+ else
51
+ if klass.meetup_auto_create_account?
52
+
53
+
54
+
55
+ user = returning(klass.new) do |u|
56
+ u.store_meetup_credentials!(
57
+ :token => access_token.token,
58
+ :uid => meetup_user_attributes['id']
59
+ )
60
+ u.on_before_meetup_auto_create(meetup_user_attributes)
61
+ end
62
+
63
+ begin
64
+
65
+
66
+ user.save(true)
67
+ user.on_after_meetup_connect(meetup_user_attributes)
68
+
69
+
70
+ success!(user)
71
+ rescue
72
+ fail!(:meetup_invalid)
73
+ end
74
+ else
75
+ fail!(:meetup_invalid)
76
+ end
77
+ end
78
+
79
+ rescue => e
80
+ fail!(e.message)
81
+ end
82
+ end
83
+
84
+
85
+
86
+
87
+ protected
88
+ def valid_controller?
89
+ # params[:controller] == 'sessions'
90
+ mapping.controllers[:sessions] == params[:controller]
91
+ end
92
+
93
+ def valid_params?
94
+ params[:code].present?
95
+ end
96
+
26
97
  end
27
98
  end
28
99
  end
29
100
  end
30
101
 
31
- Warden::Strategies.add(:meetup_authenticatable, Devise::Strategies::MeetupAuthenticatable)
102
+ Warden::Strategies.add(:meetup_authenticatable, Devise::MeetupAuthenticatable::Strategies::MeetupAuthenticatable)
32
103
 
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require 'devise/mapping'
3
+
4
+ module Devise #:nodoc:
5
+ module MeetupAuthenticatable #:nodoc:
6
+
7
+ # OAuth2 view helpers to easily add the link to the OAuth2 connection popup and also the necessary JS code.
8
+ #
9
+ module Helpers
10
+
11
+ # Creates the link to
12
+ def link_to_meetup(link_text, options={})
13
+
14
+
15
+ session_sign_in_url = Devise::session_sign_in_url(request,::Devise.mappings[:user])
16
+
17
+ link_to link_text, Devise::meetup_client.request_token.authorize_url
18
+
19
+ end
20
+
21
+
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+ ::ActionView::Base.send :include, Devise::MeetupAuthenticatable::Helpers
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_meetup_authenticatable
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 23
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
- - 1
8
+ - 2
8
9
  - 0
9
- version: 0.1.0
10
+ version: 0.2.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - matthew jording
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-07-31 00:00:00 -04:00
18
+ date: 2010-09-07 00:00:00 -04:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
@@ -25,6 +26,7 @@ dependencies:
25
26
  requirements:
26
27
  - - ">="
27
28
  - !ruby/object:Gem::Version
29
+ hash: 13
28
30
  segments:
29
31
  - 1
30
32
  - 2
@@ -40,6 +42,7 @@ dependencies:
40
42
  requirements:
41
43
  - - ">="
42
44
  - !ruby/object:Gem::Version
45
+ hash: 3
43
46
  segments:
44
47
  - 0
45
48
  version: "0"
@@ -55,22 +58,15 @@ extra_rdoc_files:
55
58
  - LICENSE
56
59
  - README.rdoc
57
60
  files:
58
- - .document
59
- - .gitignore
60
- - LICENSE
61
- - README.rdoc
62
- - Rakefile
63
- - VERSION
64
- - features/devise_meetup_authenticatable.feature
65
- - features/step_definitions/devise_meetup_authenticatable_steps.rb
66
- - features/support/env.rb
67
61
  - lib/devise_meetup_authenticatable.rb
68
62
  - lib/devise_meetup_authenticatable/model.rb
69
63
  - lib/devise_meetup_authenticatable/routes.rb
70
64
  - lib/devise_meetup_authenticatable/schema.rb
71
65
  - lib/devise_meetup_authenticatable/strategy.rb
66
+ - lib/devise_meetup_authenticatable/view_helpers.rb
67
+ - LICENSE
68
+ - README.rdoc
72
69
  - spec/devise_meetup_authenticatable_spec.rb
73
- - spec/spec.opts
74
70
  - spec/spec_helper.rb
75
71
  has_rdoc: true
76
72
  homepage: http://github.com/OpenGotham/devise_meetup_authenticatable
@@ -86,6 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
82
  requirements:
87
83
  - - ">="
88
84
  - !ruby/object:Gem::Version
85
+ hash: 3
89
86
  segments:
90
87
  - 0
91
88
  version: "0"
@@ -94,6 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
91
  requirements:
95
92
  - - ">="
96
93
  - !ruby/object:Gem::Version
94
+ hash: 3
97
95
  segments:
98
96
  - 0
99
97
  version: "0"
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE
data/.gitignore DELETED
@@ -1,21 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
data/Rakefile DELETED
@@ -1,70 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "devise_meetup_authenticatable"
8
- gem.summary = %Q{meetups oauth strategy captured for devise}
9
- gem.description = %Q{meetups oauth strategy captured for devise}
10
- gem.email = "mjording@opengotham.com"
11
- gem.homepage = "http://github.com/OpenGotham/devise_meetup_authenticatable"
12
- gem.authors = ["matthew jording"]
13
- gem.add_development_dependency "rspec", ">= 1.2.9"
14
- gem.add_development_dependency "cucumber", ">= 0"
15
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
- end
17
- Jeweler::GemcutterTasks.new
18
- rescue LoadError
19
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
- end
21
-
22
- require 'spec/rake/spectask'
23
- Spec::Rake::SpecTask.new(:spec) do |spec|
24
- spec.libs << 'lib' << 'spec'
25
- spec.spec_files = FileList['spec/**/*_spec.rb']
26
- end
27
-
28
- Spec::Rake::SpecTask.new(:rcov) do |spec|
29
- spec.libs << 'lib' << 'spec'
30
- spec.pattern = 'spec/**/*_spec.rb'
31
- spec.rcov = true
32
- end
33
-
34
- task :spec => :check_dependencies
35
-
36
- begin
37
- require 'cucumber/rake/task'
38
- Cucumber::Rake::Task.new(:features)
39
-
40
- task :features => :check_dependencies
41
- rescue LoadError
42
- task :features do
43
- abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
44
- end
45
- end
46
-
47
- begin
48
- require 'reek/adapters/rake_task'
49
- Reek::RakeTask.new do |t|
50
- t.fail_on_error = true
51
- t.verbose = false
52
- t.source_files = 'lib/**/*.rb'
53
- end
54
- rescue LoadError
55
- task :reek do
56
- abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
57
- end
58
- end
59
-
60
- task :default => :spec
61
-
62
- require 'rake/rdoctask'
63
- Rake::RDocTask.new do |rdoc|
64
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
65
-
66
- rdoc.rdoc_dir = 'rdoc'
67
- rdoc.title = "devise_meetup_authenticatable #{version}"
68
- rdoc.rdoc_files.include('README*')
69
- rdoc.rdoc_files.include('lib/**/*.rb')
70
- end
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.0
@@ -1,9 +0,0 @@
1
- Feature: something something
2
- In order to something something
3
- A user something something
4
- something something something
5
-
6
- Scenario: something something
7
- Given inspiration
8
- When I create a sweet new gem
9
- Then everyone should see how awesome I am
@@ -1,4 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
- require 'devise_meetup_authenticatable'
3
-
4
- require 'spec/expectations'
data/spec/spec.opts DELETED
@@ -1 +0,0 @@
1
- --color