Commit 22a9fad0d7025507b1f0b055fa021bcb5b43c2ed

Authored by Gabriel Silva
1 parent f919a26e

Adds federation behavior to OAuthClient plugin

- Logs in with different providers without creating a Noosfero user
- Overrides user info based on the service
- Adds option to enable the plugin federation

Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com>
Signed-off-by: Sabryna Sousa <sabryna.sousa1323@gmail.com>
Signed-off-by: Victor Navarro <victor.matias.navarro@gmail.com>
Signed-off-by: Vitor Barbosa <vitornga15@gmail.com>
Signed-off-by: Artur Bersan de Faria <artur_bersan@hotmail.com>
Signed-off-by: Matheus Miranda <matheusmirandalacerda@gmail.com>
Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>
Signed-off-by: Dylan Guedes <djmgguedes@gmail.com>
Showing 33 changed files with 534 additions and 87 deletions   Show diff stats
app/controllers/public/account_controller.rb
@@ -154,7 +154,7 @@ class AccountController &lt; ApplicationController @@ -154,7 +154,7 @@ class AccountController &lt; ApplicationController
154 154
155 # action to perform logout from the application 155 # action to perform logout from the application
156 def logout 156 def logout
157 - if logged_in? 157 + if logged_in? && self.current_user.id.present?
158 self.current_user.forget_me 158 self.current_user.forget_me
159 end 159 end
160 reset_session 160 reset_session
app/models/external_person.rb
@@ -122,6 +122,10 @@ class ExternalPerson &lt; ActiveRecord::Base @@ -122,6 +122,10 @@ class ExternalPerson &lt; ActiveRecord::Base
122 "#{jid(options)}/#{self.name}" 122 "#{jid(options)}/#{self.name}"
123 end 123 end
124 124
  125 + def name
  126 + "#{self[:name]}@#{self.source}"
  127 + end
  128 +
125 class ExternalPerson::Image 129 class ExternalPerson::Image
126 def initialize(path) 130 def initialize(path)
127 @path = path 131 @path = path
@@ -198,7 +202,10 @@ class ExternalPerson &lt; ActiveRecord::Base @@ -198,7 +202,10 @@ class ExternalPerson &lt; ActiveRecord::Base
198 relationships_cache_key: '', is_member_of?: false, follows?: false, 202 relationships_cache_key: '', is_member_of?: false, follows?: false,
199 each_friend: nil, is_last_admin?: false, is_last_admin_leaving?: false, 203 each_friend: nil, is_last_admin?: false, is_last_admin_leaving?: false,
200 leave: nil, last_notification: nil, notification_time: 0, notifier: nil, 204 leave: nil, last_notification: nil, notification_time: 0, notifier: nil,
201 - remove_suggestion: nil, allow_invitation_from?: false 205 + remove_suggestion: nil, allow_invitation_from?: false,
  206 + tracked_actions: ActionTracker::Record.none, follow: [],
  207 + update_profile_circles: ProfileFollower.none, unfollow: ProfileFollower.none,
  208 + remove_profile_from_circle: ProfileFollower.none, followed_profiles: Profile.none
202 } 209 }
203 210
204 derivated_methods = generate_derivated_methods(methods_and_responses) 211 derivated_methods = generate_derivated_methods(methods_and_responses)
@@ -255,7 +262,8 @@ class ExternalPerson &lt; ActiveRecord::Base @@ -255,7 +262,8 @@ class ExternalPerson &lt; ActiveRecord::Base
255 {}, followed_by?: false, display_private_info_to?: true, can_view_field?: 262 {}, followed_by?: false, display_private_info_to?: true, can_view_field?:
256 true, remove_from_suggestion_list: nil, layout_template: 'default', 263 true, remove_from_suggestion_list: nil, layout_template: 'default',
257 is_admin?: false, add_friend: false, follows?: false, is_a_friend?: false, 264 is_admin?: false, add_friend: false, follows?: false, is_a_friend?: false,
258 - already_request_friendship?: false 265 + already_request_friendship?: false, allow_followers: false,
  266 + in_social_circle: false, in_circle: false
259 } 267 }
260 268
261 derivated_methods = generate_derivated_methods(methods_and_responses) 269 derivated_methods = generate_derivated_methods(methods_and_responses)
features/external_login.feature
@@ -7,8 +7,8 @@ Feature: external login @@ -7,8 +7,8 @@ Feature: external login
7 Scenario: login from portal homepage 7 Scenario: login from portal homepage
8 Given feature "allow_change_of_redirection_after_login" is disabled on environment 8 Given feature "allow_change_of_redirection_after_login" is disabled on environment
9 And the following external environments 9 And the following external environments
10 - | identifier | name | url |  
11 - | test | Test | http://federated.noosfero.org | 10 + | identifier | name | url |
  11 + | test | Test | http://federated.noosfero.org/ |
12 And the following external users 12 And the following external users
13 | login | 13 | login |
14 | joaosilva@federated.noosfero.org | 14 | joaosilva@federated.noosfero.org |
@@ -26,8 +26,8 @@ Feature: external login @@ -26,8 +26,8 @@ Feature: external login
26 Scenario: not login from portal homepage 26 Scenario: not login from portal homepage
27 Given feature "allow_change_of_redirection_after_login" is disabled on environment 27 Given feature "allow_change_of_redirection_after_login" is disabled on environment
28 And the following external environments 28 And the following external environments
29 - | identifier | name | url |  
30 - | test | Test | http://federated.noosfero.org | 29 + | identifier | name | url |
  30 + | test | Test | http://federated.noosfero.org/ |
31 And I am not logged in 31 And I am not logged in
32 And I go to the homepage 32 And I go to the homepage
33 And I follow "Login" 33 And I follow "Login"
plugins/oauth_client/controllers/oauth_client_plugin_admin_controller.rb
1 class OauthClientPluginAdminController < AdminController 1 class OauthClientPluginAdminController < AdminController
2 2
3 def index 3 def index
  4 + @config = OauthClientPlugin::Config.instance
4 end 5 end
5 6
6 def new 7 def new
@@ -13,6 +14,11 @@ class OauthClientPluginAdminController &lt; AdminController @@ -13,6 +14,11 @@ class OauthClientPluginAdminController &lt; AdminController
13 redirect_to :action => 'index' 14 redirect_to :action => 'index'
14 end 15 end
15 16
  17 + def update_configs
  18 + OauthClientPlugin::Config.instance.update_attributes(params[:oauth_client_config])
  19 + redirect_to :action => 'index'
  20 + end
  21 +
16 def edit 22 def edit
17 @provider = params[:id] ? environment.oauth_providers.find(params[:id]) : environment.oauth_providers.new 23 @provider = params[:id] ? environment.oauth_providers.find(params[:id]) : environment.oauth_providers.new
18 if request.post? 24 if request.post?
@@ -24,4 +30,8 @@ class OauthClientPluginAdminController &lt; AdminController @@ -24,4 +30,8 @@ class OauthClientPluginAdminController &lt; AdminController
24 end 30 end
25 end 31 end
26 32
  33 + def edit_login_option
  34 + option = params['oauth_client_plugin_option']
  35 + end
  36 +
27 end 37 end
plugins/oauth_client/controllers/public/oauth_client_plugin_public_controller.rb
@@ -3,9 +3,15 @@ class OauthClientPluginPublicController &lt; PublicController @@ -3,9 +3,15 @@ class OauthClientPluginPublicController &lt; PublicController
3 skip_before_filter :login_required 3 skip_before_filter :login_required
4 4
5 def callback 5 def callback
6 - auth = request.env["omniauth.auth"]  
7 - auth_user = environment.users.where(email: auth.info.email).first  
8 - if auth_user then login auth_user.person else signup auth end 6 + auth_data = request.env["omniauth.auth"]
  7 + oauth_params = request.env["omniauth.params"]
  8 +
  9 + if oauth_params && oauth_params["action"] == "external_login"
  10 + external_person_login(auth_data)
  11 + else
  12 + auth_user = environment.users.where(email: auth_data.info.email).first
  13 + if auth_user then login(auth_user.person) else signup(auth_data) end
  14 + end
9 end 15 end
10 16
11 def failure 17 def failure
@@ -20,12 +26,45 @@ class OauthClientPluginPublicController &lt; PublicController @@ -20,12 +26,45 @@ class OauthClientPluginPublicController &lt; PublicController
20 26
21 protected 27 protected
22 28
23 - def login person 29 + def external_person_login(auth_data)
24 provider = OauthClientPlugin::Provider.find(session[:provider_id]) 30 provider = OauthClientPlugin::Provider.find(session[:provider_id])
25 - auth = person.oauth_auths.where(provider_id: provider.id).first  
26 - auth ||= person.oauth_auths.create! profile: person, provider: provider, enabled: true  
27 - if auth.enabled? && provider.enabled?  
28 - self.current_user = person.user 31 +
  32 + user = User.new(email: auth_data.info.email, login: auth_data.info.name.to_slug)
  33 + person = OauthClientPlugin::OauthExternalPerson.find_or_create_by(
  34 + identifier: auth_data.info.nickname || user.login,
  35 + name: auth_data.info.name,
  36 + created_at: Time.now,
  37 + source: provider.site || auth_data.provider,
  38 + email: user.email
  39 + )
  40 + user.external_person_id = person.id
  41 +
  42 + oauth_auth = person.oauth_auth
  43 + oauth_data = { profile: person, provider: provider, enabled: true,
  44 + external_person_uid: auth_data.uid, external_person_image_url: auth_data.info.image }
  45 + oauth_auth ||= OauthClientPlugin::Auth.create_for_strategy(provider.strategy, oauth_data)
  46 + create_session(user, oauth_auth)
  47 + end
  48 +
  49 + def signup(auth_data)
  50 + session[:oauth_data] = auth_data
  51 + username = auth_data.info.email.split('@').first
  52 + name = auth_data.info.name
  53 + name ||= auth_data.extra && auth_data.extra.raw_info ? auth_data.extra.raw_info.name : ''
  54 + redirect_to :controller => :account, :action => :signup, :user => {:login => username, :email => auth_data.info.email}, :profile_data => {:name => name}
  55 + end
  56 +
  57 + def login(person)
  58 + auth = person.oauth_auths.find_or_create_by(profile: person,
  59 + provider_id: session[:provider_id])
  60 + create_session(person.user, auth)
  61 + end
  62 +
  63 + def create_session(user, oauth_auth)
  64 + provider = OauthClientPlugin::Provider.find(session[:provider_id])
  65 +
  66 + if oauth_auth.allow_login?
  67 + self.current_user = user
29 else 68 else
30 session[:notice] = _("Can't login with %s") % provider.name 69 session[:notice] = _("Can't login with %s") % provider.name
31 end 70 end
@@ -33,12 +72,4 @@ class OauthClientPluginPublicController &lt; PublicController @@ -33,12 +72,4 @@ class OauthClientPluginPublicController &lt; PublicController
33 redirect_to :controller => :account, :action => :login 72 redirect_to :controller => :account, :action => :login
34 end 73 end
35 74
36 - def signup(auth)  
37 - login = auth.info.email.split('@').first  
38 - session[:oauth_data] = auth  
39 - name = auth.info.name  
40 - name ||= auth.extra && auth.extra.raw_info ? auth.extra.raw_info.name : ''  
41 - redirect_to :controller => :account, :action => :signup, :user => {:login => login, :email => auth.info.email}, :profile_data => {:name => name}  
42 - end  
43 -  
44 end 75 end
plugins/oauth_client/db/migrate/20160714113820_create_oauth_client_plugin_config.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class CreateOauthClientPluginConfig < ActiveRecord::Migration
  2 +
  3 + def change
  4 + create_table :oauth_client_plugin_configs do |t|
  5 + t.belongs_to :environment
  6 + t.boolean :allow_external_login, :default => false
  7 + end
  8 + end
  9 +end
plugins/oauth_client/db/migrate/20160720165808_add_external_profile_to_oauth_auth.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +class AddExternalProfileToOauthAuth < ActiveRecord::Migration
  2 + def up
  3 + add_column :oauth_client_plugin_auths, :profile_type, :string
  4 + add_index :oauth_client_plugin_auths, :profile_type
  5 +
  6 + add_column :oauth_client_plugin_auths, :external_person_uid, :string
  7 + add_column :oauth_client_plugin_auths, :external_person_image_url, :string
  8 +
  9 + change_column_default :oauth_client_plugin_auths, :enabled, true
  10 + end
  11 +
  12 + def down
  13 + remove_index :oauth_client_plugin_auths, :profile_type
  14 + remove_column :oauth_client_plugin_auths, :profile_type
  15 +
  16 + remove_column :oauth_client_plugin_auths, :external_person_uid
  17 + remove_column :oauth_client_plugin_auths, :external_person_image_url
  18 +
  19 + change_column_default :oauth_client_plugin_auths, :enabled, nil
  20 + end
  21 +end
plugins/oauth_client/db/migrate/20160809181708_adds_type_to_external_person.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddsTypeToExternalPerson < ActiveRecord::Migration
  2 + def up
  3 + add_column :external_people, :type, :string
  4 + end
  5 +
  6 + def down
  7 + remove_column :external_people, :type
  8 + end
  9 +end
plugins/oauth_client/lib/ext/environment.rb
1 require_dependency 'environment' 1 require_dependency 'environment'
2 2
3 class Environment 3 class Environment
4 - 4 + has_one :oauth_client_plugin_configs, :class_name => 'OauthClientPlugin::Config'
5 has_many :oauth_providers, :class_name => 'OauthClientPlugin::Provider' 5 has_many :oauth_providers, :class_name => 'OauthClientPlugin::Provider'
6 -  
7 end 6 end
plugins/oauth_client/lib/ext/profile.rb
@@ -2,7 +2,7 @@ require_dependency &#39;profile&#39; @@ -2,7 +2,7 @@ require_dependency &#39;profile&#39;
2 2
3 class Profile 3 class Profile
4 4
5 - has_many :oauth_auths, foreign_key: :profile_id, class_name: 'OauthClientPlugin::Auth', dependent: :destroy 5 + has_many :oauth_auths, as: :profile, class_name: 'OauthClientPlugin::Auth', dependent: :destroy
6 has_many :oauth_providers, through: :oauth_auths, source: :provider 6 has_many :oauth_providers, through: :oauth_auths, source: :provider
7 7
8 end 8 end
plugins/oauth_client/lib/oauth_client_plugin.rb
@@ -90,6 +90,7 @@ class OauthClientPlugin &lt; Noosfero::Plugin @@ -90,6 +90,7 @@ class OauthClientPlugin &lt; Noosfero::Plugin
90 def account_controller_filters 90 def account_controller_filters
91 { 91 {
92 :type => 'before_filter', :method_name => 'signup', 92 :type => 'before_filter', :method_name => 'signup',
  93 + :options => { :only => 'signup' },
93 :block => proc { 94 :block => proc {
94 auth = session[:oauth_data] 95 auth = session[:oauth_data]
95 96
@@ -104,7 +105,7 @@ class OauthClientPlugin &lt; Noosfero::Plugin @@ -104,7 +105,7 @@ class OauthClientPlugin &lt; Noosfero::Plugin
104 end 105 end
105 106
106 def js_files 107 def js_files
107 - ["script.js"] 108 + ["script.js", "provider.js"]
108 end 109 end
109 110
110 end 111 end
plugins/oauth_client/models/oauth_client_plugin/auth.rb
1 class OauthClientPlugin::Auth < ApplicationRecord 1 class OauthClientPlugin::Auth < ApplicationRecord
2 2
3 - attr_accessible :profile, :provider, :enabled,  
4 - :access_token, :expires_in 3 + attr_accessible :profile, :provider, :provider_id, :enabled,
  4 + :access_token, :expires_in, :type, :external_person_uid,
  5 + :external_person_image_url
5 6
6 - belongs_to :profile, class_name: 'Profile' 7 + belongs_to :profile, polymorphic: true
7 belongs_to :provider, class_name: 'OauthClientPlugin::Provider' 8 belongs_to :provider, class_name: 'OauthClientPlugin::Provider'
8 9
9 - validates_presence_of :profile  
10 validates_presence_of :provider 10 validates_presence_of :provider
  11 + validates_presence_of :profile
11 validates_uniqueness_of :profile_id, scope: :provider_id 12 validates_uniqueness_of :profile_id, scope: :provider_id
12 13
13 extend ActsAsHavingSettings::ClassMethods 14 extend ActsAsHavingSettings::ClassMethods
@@ -27,4 +28,34 @@ class OauthClientPlugin::Auth &lt; ApplicationRecord @@ -27,4 +28,34 @@ class OauthClientPlugin::Auth &lt; ApplicationRecord
27 not self.expired? 28 not self.expired?
28 end 29 end
29 30
  31 + def allow_login?
  32 + self.enabled? && self.provider.enabled?
  33 + end
  34 +
  35 + def self.create_for_strategy(strategy, args = {})
  36 + namespace = self.name.split("::")[0]
  37 + class_name = "#{namespace}::#{strategy.camelize}Auth"
  38 + OauthClientPlugin::Auth.create!(args.merge(type: class_name))
  39 + end
  40 +
  41 + IMAGE_SIZES = {
  42 + :big => "150",
  43 + :thumb => "100",
  44 + :portrait => "64",
  45 + :minor => "50",
  46 + :icon => "18"
  47 + }
  48 +
  49 + # The following methods should be implemented by
  50 + # the Provider specific Auth classes
  51 + def image_url(size = nil)
  52 + nil
  53 + end
  54 + def profile_url
  55 + nil
  56 + end
  57 + def settings_url
  58 + nil
  59 + end
  60 +
30 end 61 end
plugins/oauth_client/models/oauth_client_plugin/config.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +class OauthClientPlugin::Config < ApplicationRecord
  2 +
  3 + belongs_to :environment
  4 + attr_accessible :allow_external_login, :environment_id
  5 +
  6 + class << self
  7 + def instance
  8 + environment = Environment.default
  9 + environment.oauth_client_plugin_configs || create(environment_id: environment.id)
  10 + end
  11 +
  12 + private :new
  13 + end
  14 +
  15 +end
plugins/oauth_client/models/oauth_client_plugin/facebook_auth.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class OauthClientPlugin::FacebookAuth < OauthClientPlugin::Auth
  2 +
  3 + def image_url(size = nil)
  4 + size = IMAGE_SIZES[size] || IMAGE_SIZES[:icon]
  5 + "#{self.external_person_image_url}?width=#{size}"
  6 + end
  7 +
  8 + def profile_url
  9 + "https://www.facebook.com/#{self.external_person_uid}"
  10 + end
  11 +
  12 + def settings_url
  13 + "https://www.facebook.com/settings"
  14 + end
  15 +
  16 +end
plugins/oauth_client/models/oauth_client_plugin/github_auth.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +class OauthClientPlugin::GithubAuth < OauthClientPlugin::Auth
  2 +
  3 + def image_url(size = nil)
  4 + size = IMAGE_SIZES[size] || IMAGE_SIZES[:icon]
  5 + "#{self.external_person_image_url}&size=#{size}"
  6 + end
  7 +
  8 + def profile_url
  9 + "https://www.github.com/#{self.profile.identifier}"
  10 + end
  11 +
  12 + def settings_url
  13 + "https://www.github.com/settings"
  14 + end
  15 +end
plugins/oauth_client/models/oauth_client_plugin/google_oauth2_auth.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +class OauthClientPlugin::GoogleOauth2Auth < OauthClientPlugin::Auth
  2 +
  3 + def image_url(size = nil)
  4 + size = IMAGE_SIZES[size] || IMAGE_SIZES[:icon]
  5 + "#{self.external_person_image_url}?sz=#{size}"
  6 + end
  7 +
  8 + def profile_url
  9 + "https://plus.google.com/#{self.external_person_uid}"
  10 + end
  11 +
  12 + def settings_url
  13 + "https://plus.google.com/u/0/settings"
  14 + end
  15 +end
plugins/oauth_client/models/oauth_client_plugin/noosfero_oauth2_auth.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class OauthClientPlugin::NoosferoOauth2Auth < OauthClientPlugin::Auth
  2 +
  3 + def image_url(size = "")
  4 + URI.join("http://#{self.provider.client_options[:site]}/profile/#{self.profile.identifier}/icon/", size)
  5 + end
  6 +
  7 + def profile_url
  8 + "http://#{self.profile.source}/profile/#{self.profile.identifier}"
  9 + end
  10 +
  11 + def settings_url
  12 + "http://#{self.profile.source}/myprofile/#{self.profile.identifier}"
  13 + end
  14 +end
plugins/oauth_client/models/oauth_client_plugin/oauth_external_person.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +class OauthClientPlugin::OauthExternalPerson < ExternalPerson
  2 +
  3 + has_one :oauth_auth, as: :profile, class_name: 'OauthClientPlugin::Auth', dependent: :destroy
  4 + has_one :oauth_provider, through: :oauth_auth, source: :provider
  5 +
  6 + def avatar
  7 + self.oauth_auth.image_url
  8 + end
  9 +
  10 + def image
  11 + OauthClientPlugin::OauthExternalPerson::Image.new(self.oauth_auth)
  12 + end
  13 +
  14 + def public_profile_url
  15 + self.oauth_auth.profile_url
  16 + end
  17 +
  18 + def url
  19 + self.oauth_auth.profile_url
  20 + end
  21 +
  22 + def admin_url
  23 + self.oauth_auth.settings_url
  24 + end
  25 +
  26 + class OauthClientPlugin::OauthExternalPerson::Image < ExternalPerson::Image
  27 + def initialize(oauth_auth)
  28 + @oauth_auth = oauth_auth
  29 + end
  30 +
  31 + def public_filename(size = nil)
  32 + URI(@oauth_auth.image_url(size))
  33 + end
  34 + end
  35 +end
plugins/oauth_client/models/oauth_client_plugin/provider.rb
@@ -4,6 +4,8 @@ class OauthClientPlugin::Provider &lt; ApplicationRecord @@ -4,6 +4,8 @@ class OauthClientPlugin::Provider &lt; ApplicationRecord
4 4
5 validates_presence_of :name, :strategy 5 validates_presence_of :name, :strategy
6 6
  7 + validate :noosfero_provider_must_have_a_site
  8 +
7 extend ActsAsHavingImage::ClassMethods 9 extend ActsAsHavingImage::ClassMethods
8 acts_as_having_image 10 acts_as_having_image
9 11
@@ -19,4 +21,9 @@ class OauthClientPlugin::Provider &lt; ApplicationRecord @@ -19,4 +21,9 @@ class OauthClientPlugin::Provider &lt; ApplicationRecord
19 21
20 scope :enabled, -> { where enabled: true } 22 scope :enabled, -> { where enabled: true }
21 23
  24 + def noosfero_provider_must_have_a_site
  25 + if self.strategy == 'noosfero_oauth2' && (self.client_options.nil? || self.client_options[:site].blank?)
  26 + self.errors.add(:site, "A Noosfero provider must have a site")
  27 + end
  28 + end
22 end 29 end
plugins/oauth_client/models/oauth_client_plugin/twitter_auth.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +class OauthClientPlugin::TwitterAuth < OauthClientPlugin::Auth
  2 +
  3 + IMAGE_SIZES = {
  4 + :big => "",
  5 + :thumb => "_bigger",
  6 + :portrait => "_normal",
  7 + :minor => "_normal",
  8 + :icon => "_mini"
  9 + }
  10 +
  11 + def image_url(size = nil)
  12 + size = IMAGE_SIZES[size] || IMAGE_SIZES[:icon]
  13 + self.external_person_image_url.gsub("_normal", size)
  14 + end
  15 +
  16 + def profile_url
  17 + "https://twitter.com/#{self.profile.identifier}"
  18 + end
  19 +
  20 + def settings_url
  21 + "https://twitter.com/settings"
  22 + end
  23 +end
plugins/oauth_client/public/provider.js 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +function toggle_strategy(strategyName) {
  2 + if (strategyName == "noosfero_oauth2") {
  3 + $(".client-url").addClass("required-field");
  4 + } else {
  5 + $(".client-url").removeClass("required-field");
  6 + }
  7 +}
  8 +
  9 +$(document).on("change", "select#oauth_client_plugin_provider_strategy", function() {
  10 + var selectedOption = $(this).find(":selected").text();
  11 + toggle_strategy(selectedOption);
  12 +});
plugins/oauth_client/test/functional/account_controller_test.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +require 'test_helper'
  2 +
  3 +class AccountControllerTest < ActionController::TestCase
  4 + def setup
  5 + external_person = ExternalPerson.create!(identifier: 'johnlock',
  6 + name: 'John Locke',
  7 + source: 'anerenvironment.org',
  8 + email: 'john@locke.org',
  9 + created_at: Date.yesterday
  10 + )
  11 + session[:external] = external_person.id
  12 + end
  13 +
  14 + should "not create an User when logging out" do
  15 + assert_no_difference 'User.count' do
  16 + get :logout
  17 + end
  18 + end
  19 +end
plugins/oauth_client/test/functional/oauth_client_plugin_public_controller_test.rb
@@ -5,76 +5,97 @@ class OauthClientPluginPublicControllerTest &lt; ActionController::TestCase @@ -5,76 +5,97 @@ class OauthClientPluginPublicControllerTest &lt; ActionController::TestCase
5 def setup 5 def setup
6 @auth = mock 6 @auth = mock
7 @auth.stubs(:info).returns(mock) 7 @auth.stubs(:info).returns(mock)
  8 + @auth.info.stubs(:email).returns("user@email.com")
  9 + @auth.info.stubs(:name).returns("User")
  10 + @auth.info.stubs(:nickname).returns("user")
  11 + @auth.info.stubs(:image).returns("url.to.image.com")
  12 + @auth.stubs(:provider).returns("testprovider")
  13 + @auth.stubs(:uid).returns("jh12j3h12kjh312")
  14 +
8 request.env["omniauth.auth"] = @auth 15 request.env["omniauth.auth"] = @auth
9 @environment = Environment.default 16 @environment = Environment.default
10 - @provider = OauthClientPlugin::Provider.create!(:name => 'provider', :strategy => 'provider', :enabled => true) 17 + @provider = OauthClientPlugin::Provider.create!(:name => 'provider', :strategy => 'github', :enabled => true)
  18 +
  19 + session[:provider_id] = provider.id
11 end 20 end
12 attr_reader :auth, :environment, :provider 21 attr_reader :auth, :environment, :provider
13 22
14 should 'redirect to signup when user is not found' do 23 should 'redirect to signup when user is not found' do
15 - auth.info.stubs(:email).returns("xyz123@noosfero.org")  
16 - auth.info.stubs(:name).returns('xyz123')  
17 - session[:provider_id] = provider.id  
18 -  
19 get :callback 24 get :callback
20 assert_match /.*\/account\/signup/, @response.redirect_url 25 assert_match /.*\/account\/signup/, @response.redirect_url
21 end 26 end
22 27
23 - should 'redirect to login when user is found' do  
24 - user = create_user  
25 - auth.info.stubs(:email).returns(user.email)  
26 - auth.info.stubs(:name).returns(user.name)  
27 - session[:provider_id] = provider.id 28 + should 'login when user already signed up' do
  29 + create_user(@auth.info.name, email: @auth.info.email)
28 30
29 get :callback 31 get :callback
30 - assert_redirected_to :controller => :account, :action => :login  
31 - assert_equal user.id, session[:user] 32 + assert session[:user].present?
32 end 33 end
33 34
34 - should 'do not login when the provider is disabled' do  
35 - user = create_user  
36 - auth.info.stubs(:email).returns(user.email)  
37 - auth.info.stubs(:name).returns(user.name)  
38 - session[:provider_id] = provider.id 35 + should 'not login when user already signed up and the provider is disabled' do
  36 + create_user(@auth.info.name, email: @auth.info.email)
39 provider.update_attribute(:enabled, false) 37 provider.update_attribute(:enabled, false)
40 38
41 get :callback 39 get :callback
42 - assert_redirected_to :controller => :account, :action => :login  
43 - assert_equal nil, session[:user] 40 + assert session[:user].nil?
44 end 41 end
45 42
46 - should 'do not login when the provider is disabled for a user' do  
47 - user = create_user  
48 - auth.info.stubs(:email).returns(user.email)  
49 - auth.info.stubs(:name).returns(user.name)  
50 - session[:provider_id] = provider.id  
51 - user.person.oauth_auths.create!(profile: user.person, provider: provider, enabled: false) 43 + should 'not login when user already signed up and the provider is disabled for him' do
  44 + create_user(@auth.info.name, email: @auth.info.email)
  45 + OauthClientPlugin::Auth.any_instance.stubs(:enabled?).returns(false)
52 46
53 get :callback 47 get :callback
54 - assert_redirected_to :controller => :account, :action => :login  
55 - assert_equal nil, session[:user] 48 + assert session[:user].nil?
56 end 49 end
57 50
58 - should 'save provider when an user login with it' do  
59 - user = create_user  
60 - auth.info.stubs(:email).returns(user.email)  
61 - auth.info.stubs(:name).returns(user.name)  
62 - session[:provider_id] = provider.id 51 + should 'not duplicate oauth_auths when the same provider is used several times' do
  52 + user = create_user(@auth.info.name, email: @auth.info.email)
63 53
64 get :callback 54 get :callback
65 - assert_equal [provider], user.oauth_providers 55 + assert_no_difference 'user.oauth_auths.count' do
  56 + 3.times { get :callback }
  57 + end
66 end 58 end
67 59
68 - should 'do not duplicate relations between an user and a provider when the same provider was used again in a login' do  
69 - user = create_user  
70 - auth.info.stubs(:email).returns(user.email)  
71 - auth.info.stubs(:name).returns(user.name)  
72 - session[:provider_id] = provider.id 60 + should 'perform external login using provider when url param is present' do
  61 + request.env["omniauth.params"] = {"action" => "external_login"}
73 62
74 get :callback 63 get :callback
75 - assert_no_difference 'user.oauth_auths.count' do  
76 - 3.times { get :callback } 64 + assert_redirected_to :controller => :account, :action => :login
  65 + assert session[:external].present?
  66 + end
  67 +
  68 + should 'not create an user when performing external login' do
  69 + request.env["omniauth.params"] = {"action" => "external_login"}
  70 +
  71 + assert_no_difference 'User.count' do
  72 + get :callback
77 end 73 end
78 end 74 end
79 75
  76 + should 'not perform external login when the provider is disabled' do
  77 + request.env["omniauth.params"] = {"action" => "external_login"}
  78 + provider.update_attribute(:enabled, false)
  79 +
  80 + get :callback
  81 + assert_redirected_to :controller => :account, :action => :login
  82 + assert session[:external].nil?
  83 + end
  84 +
  85 + should 'not perform external login when the provider is disabled for a user' do
  86 + request.env["omniauth.params"] = {"action" => "external_login"}
  87 + OauthClientPlugin::GithubAuth.any_instance.stubs(:enabled?).returns(false)
  88 +
  89 + get :callback
  90 + assert_redirected_to :controller => :account, :action => :login
  91 + assert session[:external].nil?
  92 + end
  93 +
  94 + should 'save provider when an external person logs in with it' do
  95 + request.env["omniauth.params"] = {"action" => "external_login"}
  96 +
  97 + get :callback
  98 + external_person = OauthClientPlugin::OauthExternalPerson.find_by(identifier: auth.info.nickname)
  99 + assert_equal provider, external_person.oauth_auth.provider
  100 + end
80 end 101 end
plugins/oauth_client/test/unit/auth_test.rb 0 → 100644
@@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
  1 +require 'test_helper'
  2 +
  3 +class AuthTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @person = fast_create(Person)
  7 + @provider = fast_create(OauthClientPlugin::Provider, name: "GitHub")
  8 + @external_person = fast_create(ExternalPerson, name: "testuser", email: "test@email.com",
  9 + identifier: "testuser")
  10 +
  11 + OauthClientPlugin::Auth.any_instance.stubs(:external_person_image_url).returns("http://some.host/image")
  12 + OauthClientPlugin::Auth.any_instance.stubs(:external_person_uid).returns("j4b25cj234hb5n235")
  13 + OauthClientPlugin::Provider.any_instance.stubs(:client_options).returns({site: "http://host.com"})
  14 + end
  15 +
  16 + should "not create an auth without a related profile or external person" do
  17 + auth = OauthClientPlugin::Auth.new(provider: @provider)
  18 + assert_not auth.valid?
  19 + end
  20 +
  21 + should "create an auth with an external person" do
  22 + auth = OauthClientPlugin::Auth.create!(profile: @external_person,
  23 + provider: @provider)
  24 + assert auth.id.present?
  25 + end
  26 +
  27 + should "create an auth with a profile" do
  28 + auth = OauthClientPlugin::Auth.create!(profile: @person, provider: @provider)
  29 + assert auth.id.present?
  30 + end
  31 +
  32 + should "create an auth for a custom provider" do
  33 + auth = OauthClientPlugin::Auth.create_for_strategy("github", provider: @provider,
  34 + profile: @person)
  35 + assert auth.id.present?
  36 + assert auth.is_a? OauthClientPlugin::GithubAuth
  37 + end
  38 +
  39 + STRATEGIES = %w[facebook github google_oauth2 noosfero_oauth2 twitter]
  40 + STRATEGIES.each do |strategy|
  41 + should "override the external person's image url for #{strategy} strategy" do
  42 + auth = OauthClientPlugin::Auth.create_for_strategy(strategy, provider: @provider,
  43 + profile: @external_person)
  44 + assert_not auth.image_url.nil?
  45 + end
  46 +
  47 + should "override the external person's profile url for #{strategy} strategy" do
  48 + auth = OauthClientPlugin::Auth.create_for_strategy(strategy, provider: @provider,
  49 + profile: @external_person)
  50 + assert_not auth.profile_url.nil?
  51 + end
  52 +
  53 + should "override the external person's profile settings url for #{strategy} strategy" do
  54 + auth = OauthClientPlugin::Auth.create_for_strategy(strategy, provider: @provider,
  55 + profile: @external_person)
  56 + assert_not auth.settings_url.nil?
  57 + end
  58 + end
  59 +
  60 +end
plugins/oauth_client/test/unit/oauth_external_person_test.rb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +
  2 +require 'test_helper'
  3 +
  4 +class OauthExternalPersonTest < ActiveSupport::TestCase
  5 +
  6 + def setup
  7 + provider = fast_create(OauthClientPlugin::Provider, name: "GitHub")
  8 + @external_person = fast_create(ExternalPerson, name: "testuser", email: "test@email.com",
  9 + identifier: "testuser")
  10 + OauthClientPlugin::GithubAuth.create!(profile: @external_person, provider: provider)
  11 +
  12 + @oauth_external_person = fast_create(OauthClientPlugin::OauthExternalPerson,
  13 + name: "testuser", email: "test@email.com",
  14 + identifier: "testuser")
  15 + OauthClientPlugin::GithubAuth.create!(profile: @oauth_external_person,
  16 + provider: provider)
  17 + end
  18 +
  19 + should "not orverride info from a regular external person" do
  20 + assert_not_equal @external_person.avatar, @oauth_external_person.avatar
  21 + assert_not_equal @external_person.url, @oauth_external_person.url
  22 + assert_not_equal @external_person.admin_url, @oauth_external_person.admin_url
  23 + assert_not_equal @external_person.public_profile_url,
  24 + @oauth_external_person.public_profile_url
  25 + end
  26 +
  27 + should "not override the Image class from a regular external person" do
  28 + assert @external_person.image.is_a? ExternalPerson::Image
  29 + assert @oauth_external_person.image.is_a? OauthClientPlugin::OauthExternalPerson::Image
  30 + end
  31 +end
plugins/oauth_client/test/unit/provider_test.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +require 'test_helper'
  2 +
  3 +class ProviderTest < ActiveSupport::TestCase
  4 +
  5 + should "only create a noosfero provider with a site" do
  6 + provider = OauthClientPlugin::Provider.new(:name => 'noosfero', :strategy => 'noosfero_oauth2')
  7 + assert_not provider.valid?
  8 +
  9 + provider.client_options = { :site => "http://noosfero.org" }
  10 + assert provider.valid?
  11 + end
  12 +
  13 + should "create a regular provider without a site" do
  14 + provider = OauthClientPlugin::Provider.new(:name => 'github', :strategy => 'github')
  15 + assert provider.valid?
  16 + end
  17 +end
plugins/oauth_client/views/account/_oauth_signup.html.erb
1 <%= hidden_field_tag 'return_to', '/' %> 1 <%= hidden_field_tag 'return_to', '/' %>
2 2
3 <style> 3 <style>
4 - #signup-password, #signup-password-confirmation, #signup-email { 4 + #signup-email {
5 display: none; 5 display: none;
6 } 6 }
7 </style> 7 </style>
plugins/oauth_client/views/auth/_generate_providers_links.html.erb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<% providers.each do |provider| %>
  2 + <span class="provider">
  3 + <%= link_to provider.image ? image_tag(provider.image.public_filename) : provider.name, "/plugin/oauth_client/#{provider.strategy}?id=#{provider.id}&action=#{action}", :class => provider.strategy, :title => provider.name %>
  4 + </span>
  5 +<% end %>
plugins/oauth_client/views/auth/_oauth_login.html.erb
1 <div class="oauth-login"> 1 <div class="oauth-login">
2 <% unless providers.empty? %> 2 <% unless providers.empty? %>
3 - <%= _('Login with:') %>  
4 - <% end %>  
5 - <% providers.each do |provider| %>  
6 - <span class="provider">  
7 - <%= link_to provider.image ? image_tag(provider.image.public_filename) : provider.name, "/plugin/oauth_client/#{provider.strategy}?id=#{provider.id}", :class => provider.strategy, :title => provider.name %>  
8 - </span> 3 + <div class="providers-links">
  4 + <%= _('Create an account from:') %>
  5 + <div>
  6 + <%= render :partial => 'auth/generate_providers_links', :locals => {:providers => providers, :action => ""} %>
  7 + </div>
  8 + <% if OauthClientPlugin::Config.instance.allow_external_login %>
  9 + <%= _('Login with:') %>
  10 + <div>
  11 + <%= render :partial => 'auth/generate_providers_links', :locals => {:providers => providers, :action => "external_login"} %>
  12 + </div>
  13 + <% end %>
  14 + </div>
9 <% end %> 15 <% end %>
10 16
11 <span class="provider"> 17 <span class="provider">
plugins/oauth_client/views/oauth_client_plugin_admin/_noosfero_oauth2.html.erb
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -<%= f.fields_for :client_options, OpenStruct.new(provider.options[:client_options]) do |c| %>  
2 - <div class="client-url">  
3 - <%= labelled_form_field _('Client Url'), c.text_field(:site) %>  
4 - </div>  
5 -<% end %>  
plugins/oauth_client/views/oauth_client_plugin_admin/edit.html.erb
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 </div> 8 </div>
9 9
10 <div class="name"> 10 <div class="name">
11 - <%= labelled_form_field _('Name'), f.text_field(:name) %> 11 + <%= required labelled_form_field _('Name'), f.text_field(:name) %>
12 </div> 12 </div>
13 13
14 <div class="strategy"> 14 <div class="strategy">
@@ -27,8 +27,10 @@ @@ -27,8 +27,10 @@
27 <%= labelled_form_field _('Client Secret'), f.text_field(:client_secret) %> 27 <%= labelled_form_field _('Client Secret'), f.text_field(:client_secret) %>
28 </div> 28 </div>
29 29
30 - <% if File.exists?(File.join(File.dirname(__FILE__), "_#{@provider.strategy}.html.erb")) %>  
31 - <%= render :partial => "#{@provider.strategy}", :locals => {:f => f, :provider => @provider} %> 30 + <%= f.fields_for :client_options, OpenStruct.new(@provider.options[:client_options]) do |c| %>
  31 + <div class="client-url <%= "required-field" if @provider.strategy == "noosfero_oauth2" %>" title="<%= _("The service URL. If this value is informed, it will be used to identify the users, replacing the provider name") %>">
  32 + <%= labelled_form_field _('Client Url'), c.text_field(:site) %>
  33 + </div>
32 <% end %> 34 <% end %>
33 35
34 <div class="image-icon"> 36 <div class="image-icon">
plugins/oauth_client/views/oauth_client_plugin_admin/index.html.erb
1 <h1><%= _('Oauth Client Settings') %></h1> 1 <h1><%= _('Oauth Client Settings') %></h1>
  2 +
  3 +<%= labelled_form_for @config, url: { action: "update_configs" } do |f| %>
  4 + <table>
  5 + <tr>
  6 + <%= hidden_field_tag "oauth_client_config[allow_external_login]", false %>
  7 + <td><%= _('Allow external login') %></td>
  8 + <td><%= check_box_tag "oauth_client_config[allow_external_login]", true, @config.allow_external_login %></td>
  9 + </tr>
  10 + </table>
  11 +
  12 + <div>
  13 + <%= button_bar do %>
  14 + <%= submit_button('save', _('Save changes')) %>
  15 + <%= button :back, _('Back to plugins panel'), :controller => 'plugins', :action => 'index' %>
  16 + <% end %>
  17 + </div>
  18 +<% end %>
  19 +
2 <h3><%= _('Providers') %></h3> 20 <h3><%= _('Providers') %></h3>
3 <%= button :add, _('New'), {:action => 'new'} %> 21 <%= button :add, _('New'), {:action => 'new'} %>
4 <table> 22 <table>
test/unit/external_person_test.rb
@@ -31,7 +31,9 @@ class ExternalPersonTest &lt; ActiveSupport::TestCase @@ -31,7 +31,9 @@ class ExternalPersonTest &lt; ActiveSupport::TestCase
31 31
32 should 'not be a member of any communities' do 32 should 'not be a member of any communities' do
33 community = fast_create(Community) 33 community = fast_create(Community)
34 - refute community.add_member(@external_person) 34 + assert_raise do
  35 + community.add_member(@external_person)
  36 + end
35 assert_equivalent [], @external_person.memberships 37 assert_equivalent [], @external_person.memberships
36 end 38 end
37 39