From 1575ce7b1076c35c6498aad3d25b7458e88de93d Mon Sep 17 00:00:00 2001 From: Erik Ordway Date: Thu, 23 May 2013 13:40:50 -0700 Subject: [PATCH] add cas support with a few tests --- Gemfile | 1 + .../templates/modal/login.js.handlebars | 3 ++ .../discourse/views/modal/login_view.js | 9 ++++ .../stylesheets/components/buttons.css.scss | 3 ++ .../stylesheets/foundation/variables.scss | 1 + .../users/omniauth_callbacks_controller.rb | 54 ++++++++++++++++++- app/models/cas_user_info.rb | 5 ++ app/models/site_setting.rb | 5 ++ app/models/user.rb | 1 + config/initializers/omniauth.rb | 3 ++ config/locales/client.en.yml | 3 ++ config/locales/server.en.yml | 6 +++ .../20130521210140_create_cas_user_infos.rb | 19 +++++++ .../omniauth_callbacks_controller_spec.rb | 22 +++++++- .../complete.html.erb_spec.rb | 12 +++++ 15 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 app/models/cas_user_info.rb create mode 100644 db/migrate/20130521210140_create_cas_user_infos.rb diff --git a/Gemfile b/Gemfile index d730ac6e194..459f49de6cd 100644 --- a/Gemfile +++ b/Gemfile @@ -45,6 +45,7 @@ gem 'omniauth-facebook' gem 'omniauth-twitter' gem 'omniauth-github' gem 'omniauth-browserid', git: 'https://github.com/callahad/omniauth-browserid.git', branch: 'observer_api' +gem 'omniauth-cas' gem 'oj' gem 'pg' # we had pain with the 3.2.13 upgrade so monkey patch the security fix diff --git a/app/assets/javascripts/discourse/templates/modal/login.js.handlebars b/app/assets/javascripts/discourse/templates/modal/login.js.handlebars index 79665f45be1..32f55efa98d 100644 --- a/app/assets/javascripts/discourse/templates/modal/login.js.handlebars +++ b/app/assets/javascripts/discourse/templates/modal/login.js.handlebars @@ -7,6 +7,9 @@ {{#if Discourse.SiteSettings.enable_facebook_logins}} {{/if}} + {{#if Discourse.SiteSettings.enable_cas_logins}} + + {{/if}} {{#if Discourse.SiteSettings.enable_twitter_logins}} {{/if}} diff --git a/app/assets/javascripts/discourse/views/modal/login_view.js b/app/assets/javascripts/discourse/views/modal/login_view.js index 8bf21210f47..95414e9efd5 100644 --- a/app/assets/javascripts/discourse/views/modal/login_view.js +++ b/app/assets/javascripts/discourse/views/modal/login_view.js @@ -31,6 +31,7 @@ Discourse.LoginView = Discourse.ModalBodyView.extend({ hasAtLeastOneLoginButton: function() { return Discourse.SiteSettings.enable_google_logins || Discourse.SiteSettings.enable_facebook_logins || + Discourse.SiteSettings.enable_cas_logins || Discourse.SiteSettings.enable_twitter_logins || Discourse.SiteSettings.enable_yahoo_logins || Discourse.SiteSettings.enable_github_logins || @@ -102,6 +103,14 @@ Discourse.LoginView = Discourse.ModalBodyView.extend({ return window.open(Discourse.getURL("/auth/facebook"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top); }, + casLogin: function() { + var left, top; + this.set('authenticate', 'cas'); + left = this.get('lastX') - 400; + top = this.get('lastY') - 200; + return window.open("/auth/cas", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top); + }, + openidLogin: function(provider) { var left = this.get('lastX') - 400; var top = this.get('lastY') - 200; diff --git a/app/assets/stylesheets/components/buttons.css.scss b/app/assets/stylesheets/components/buttons.css.scss index d65cd88dcbc..ca9e9c2f3b6 100644 --- a/app/assets/stylesheets/components/buttons.css.scss +++ b/app/assets/stylesheets/components/buttons.css.scss @@ -156,6 +156,9 @@ content: "f"; } } + &.cas { + background: $cas; + } &.twitter { background: $twitter; &:before { diff --git a/app/assets/stylesheets/foundation/variables.scss b/app/assets/stylesheets/foundation/variables.scss index a6bdf11a978..2f5619b7569 100644 --- a/app/assets/stylesheets/foundation/variables.scss +++ b/app/assets/stylesheets/foundation/variables.scss @@ -121,6 +121,7 @@ $black: #000 !default; $white: #fff !default; $google: #5b76f7 !default; $facebook: #3b5998 !default; +$cas: #70BA61 !default; $twitter: #00bced !default; $yahoo: #810293 !default; $github: #6d6d6d !default; diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 14cf394a733..4e45f814b2a 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -7,7 +7,7 @@ class Users::OmniauthCallbacksController < ApplicationController layout false def self.types - @types ||= Enum.new(:facebook, :twitter, :google, :yahoo, :github, :persona) + @types ||= Enum.new(:facebook, :twitter, :google, :yahoo, :github, :persona, :cas) end # need to be able to call this @@ -142,6 +142,58 @@ class Users::OmniauthCallbacksController < ApplicationController end + def create_or_sign_on_user_using_cas(auth_token) + logger.error "authtoken #{auth_token}" + email = "#{auth_token[:extra][:user]}@evergreen.edu" + username = auth_token[:extra][:user] + name = auth_token["uid"] + cas_user_id = auth_token["uid"] + + session[:authentication] = { + cas: { + cas_user_id: cas_user_id , + username: username + }, + email: email, + email_valid: true + } + + user_info = CasUserInfo.where(:cas_user_id => cas_user_id ).first + + @data = { + username: username, + name: name, + email: email, + auth_provider: "CAS", + email_valid: true + } + + if user_info + user = user_info.user + if user + unless user.active + user.active = true + user.save + end + log_on_user(user) + @data[:authenticated] = true + end + else + user = User.where(email: email).first + if user + CasUserInfo.create!(session[:authentication][:cas].merge(user_id: user.id)) + unless user.active + user.active = true + user.save + end + log_on_user(user) + @data[:authenticated] = true + end + end + + end + + def create_or_sign_on_user_using_openid(auth_token) data = auth_token[:info] diff --git a/app/models/cas_user_info.rb b/app/models/cas_user_info.rb new file mode 100644 index 00000000000..633566eb614 --- /dev/null +++ b/app/models/cas_user_info.rb @@ -0,0 +1,5 @@ +class CasUserInfo < ActiveRecord::Base + attr_accessible :email, :cas_user_id, :first_name, :gender, :last_name, :name, :user_id, :username, :link + belongs_to :user + +end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 5a770f0d2e2..1635b3185eb 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -140,6 +140,11 @@ class SiteSetting < ActiveRecord::Base setting(:facebook_app_id, '') setting(:facebook_app_secret, '') + client_setting(:enable_cas_logins, false) + setting(:cas_url, '') + setting(:cas_hostname, '') + setting(:cas_domainname, '') + client_setting(:enable_github_logins, false) setting(:github_client_id, '') setting(:github_client_secret, '') diff --git a/app/models/user.rb b/app/models/user.rb index a307ef3efc8..8b62665cbf6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -27,6 +27,7 @@ class User < ActiveRecord::Base has_one :twitter_user_info, dependent: :destroy has_one :github_user_info, dependent: :destroy + has_one :cas_user_info, dependent: :destroy belongs_to :approved_by, class_name: 'User' has_many :group_users diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 32e292f03da..18c4a8d1891 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -45,4 +45,7 @@ Rails.application.config.middleware.use OmniAuth::Builder do provider :browser_id, :name => 'persona' + provider :cas, + :host => SiteSetting.cas_hostname + end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 64ab63586fe..18eb87c4795 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -316,6 +316,9 @@ en: facebook: title: "with Facebook" message: "Authenticating with Facebook (make sure pop up blockers are not enabled)" + cas: + title: "Log In with CAS" + message: "Authenticating with CAS (make sure pop up blockers are not enabled)" yahoo: title: "with Yahoo" message: "Authenticating with Yahoo (make sure pop up blockers are not enabled)" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 1d4faa1d0e7..8d07f2d3787 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -376,6 +376,7 @@ en: queue_size_warning: 'The number of queued jobs is %{queue_size}, which is high. This could indicate a problem with the Sidekiq process(es), or you may need to add more Sidekiq workers.' memory_warning: 'Your server is running with less than 1 GB of total memory. At least 1 GB of memory is recommended.' facebook_config_warning: 'The server is configured to allow signup and log in with Facebook (enable_facebook_logins), but the app id and app secret values are not set. Go to the Site Settings and update the settings. See this guide to learn more.' + cas_config_warning: 'The server is configured to allow signup and log in with CAS (enable_facebook_logins), but the hostname and domain name values are not set.' twitter_config_warning: 'The server is configured to allow signup and log in with Twitter (enable_twitter_logins), but the key and secret values are not set. Go to the Site Settings and update the settings. See this guide to learn more.' github_config_warning: 'The server is configured to allow signup and log in with GitHub (enable_github_logins), but the client id and secret values are not set. Go to the Site Settings and update the settings. See this guide to learn more.' failing_emails_warning: 'There are %{num_failed_jobs} email jobs that failed. Check your config/environments/production.rb file and ensure that the config.action_mailer settings are correct. See the failed jobs in Sidekiq.' @@ -503,6 +504,11 @@ en: facebook_app_id: "App id for Facebook authentication, registered at https://developers.facebook.com/apps" facebook_app_secret: "App secret for Facebook authentication, registered at https://developers.facebook.com/apps" + enable_cas_logins: "Enable CAS authentication" + cas_url: "Url for cas server" + cas_hostname: "Hostname for cas server" + cas_domainname: "Domain name generated email addresses for cas server" + enable_github_logins: "Enable Github authentication, requires github_client_id and github_client_secret" github_client_id: "Client id for Github authentication, registered at https://github.com/settings/applications" github_client_secret: "Client secret for Github authentication, registered at https://github.com/settings/applications" diff --git a/db/migrate/20130521210140_create_cas_user_infos.rb b/db/migrate/20130521210140_create_cas_user_infos.rb new file mode 100644 index 00000000000..befadc56aa6 --- /dev/null +++ b/db/migrate/20130521210140_create_cas_user_infos.rb @@ -0,0 +1,19 @@ +class CreateCasUserInfos < ActiveRecord::Migration + def change + create_table :cas_user_infos do |t| + t.integer :user_id, null: false + t.string :cas_user_id, null: false + t.string :username, null: false + t.string :first_name + t.string :last_name + t.string :email + t.string :gender + t.string :name + t.string :link + + t.timestamps + end + add_index :cas_user_infos, :user_id, unique: true + add_index :cas_user_infos, :cas_user_id, unique: true + end +end diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index 4c6984475d1..37eb2959a77 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Users::OmniauthCallbacksController do let(:auth) { {info: {email: 'eviltrout@made.up.email', name: 'Robin Ward', uid: 123456789}, "extra" => {"raw_info" => {} } } } - + let(:cas_auth) {{ uid: "caluser2", extra: {user: "caluser2"} } } describe 'invalid provider' do it "fails" do @@ -54,6 +54,26 @@ describe Users::OmniauthCallbacksController do end + describe 'cas' do + + before do + request.env["omniauth.auth"] = cas_auth + end + + it "fails when cas logins are disabled" do + SiteSetting.stubs(:enable_cas_logins?).returns(false) + get :complete, provider: 'cas' + response.should_not be_success + end + + it "succeeds when cas logins are enabled" do + SiteSetting.stubs(:enable_cas_logins?).returns(true) + get :complete, provider: 'cas' + response.should be_success + end + + end + describe 'open id handler' do diff --git a/spec/views/omniauth_callbacks/complete.html.erb_spec.rb b/spec/views/omniauth_callbacks/complete.html.erb_spec.rb index c29c4e12c22..91f5a5aa72f 100644 --- a/spec/views/omniauth_callbacks/complete.html.erb_spec.rb +++ b/spec/views/omniauth_callbacks/complete.html.erb_spec.rb @@ -13,6 +13,18 @@ describe "users/omniauth_callbacks/complete.html.erb" do rendered_data["awaiting_activation"].should eq(true) end + it "renders cas data " do + assign(:data, {username: "username", :auth_provider=> "CAS", :awaiting_activation=>true}) + + render + + rendered_data = JSON.parse(rendered.match(/window.opener.Discourse.authenticationComplete\((.*)\)/)[1]) + + rendered_data["username"].should eq("username") + rendered_data["auth_provider"].should eq("CAS") + rendered_data["awaiting_activation"].should eq(true) + end + it "renders twitter data " do assign(:data, {username: "username", :auth_provider=>"Twitter", :awaiting_activation=>true})