devise_meetup_authenticatable 0.1.0 → 0.2.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.
@@ -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