From a64aff2f1c1ddc77b00211489d74fbc23c0c2fa2 Mon Sep 17 00:00:00 2001 From: Florian Unglaub Date: Fri, 3 Aug 2012 17:27:39 +0200 Subject: [PATCH 1/6] Omniauth Support --- Gemfile | 4 ++ Gemfile.lock | 32 ++++++++++++++ app/assets/stylesheets/auth_methods.scss | 10 +++++ app/assets/stylesheets/main.scss | 26 ++++++------ .../omniauth_callbacks_controller.rb | 32 +++++++++++++- app/helpers/application_helper.rb | 11 +++-- app/views/devise/sessions/new.html.erb | 13 ++++-- app/views/layouts/profile.html.haml | 2 +- app/views/profile/password.html.haml | 40 ++++++++++++------ app/views/profile/show.html.haml | 7 +++ config/gitlab.yml.example | 22 +++++++--- config/initializers/1_settings.rb | 19 ++++++--- ...803152018_add_provider_and_uid_to_users.rb | 6 +++ db/schema.rb | 32 +++++++------- .../assets/images/authbuttons/github_32.png | Bin 0 -> 1931 bytes .../assets/images/authbuttons/github_64.png | Bin 0 -> 4447 bytes .../assets/images/authbuttons/google_32.png | Bin 0 -> 1615 bytes .../assets/images/authbuttons/google_64.png | Bin 0 -> 3446 bytes .../assets/images/authbuttons/twitter_32.png | Bin 0 -> 1439 bytes .../assets/images/authbuttons/twitter_64.png | Bin 0 -> 3384 bytes 20 files changed, 195 insertions(+), 61 deletions(-) create mode 100644 app/assets/stylesheets/auth_methods.scss create mode 100644 db/migrate/20120803152018_add_provider_and_uid_to_users.rb create mode 100644 vendor/assets/images/authbuttons/github_32.png create mode 100644 vendor/assets/images/authbuttons/github_64.png create mode 100644 vendor/assets/images/authbuttons/google_32.png create mode 100644 vendor/assets/images/authbuttons/google_64.png create mode 100644 vendor/assets/images/authbuttons/twitter_32.png create mode 100644 vendor/assets/images/authbuttons/twitter_64.png diff --git a/Gemfile b/Gemfile index d2a5728f97c..12610b65821 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,10 @@ gem "mysql2" # Auth gem "devise", "~> 2.1.0" +gem 'omniauth' +gem 'omniauth-google-oauth2' +gem 'omniauth-twitter' +gem 'omniauth-github' # GITLAB patched libs gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" diff --git a/Gemfile.lock b/Gemfile.lock index 7356c35ede0..b34f401b1c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -166,6 +166,8 @@ GEM eventmachine (0.12.10) execjs (1.4.0) multi_json (~> 1.0) + faraday (0.8.1) + multipart-post (~> 1.1) ffaker (1.14.0) ffi (1.0.11) foreman (0.47.0) @@ -191,6 +193,7 @@ GEM httparty (0.8.3) multi_json (~> 1.0) multi_xml + httpauth (0.1) i18n (0.6.0) journey (1.0.4) jquery-rails (2.0.2) @@ -200,6 +203,8 @@ GEM jquery-rails railties (>= 3.1.0) json (1.7.4) + jwt (0.1.5) + multi_json (>= 1.0) kaminari (0.13.0) actionpack (>= 3.0.0) activesupport (>= 3.0.0) @@ -223,12 +228,35 @@ GEM sprockets (~> 2.0) multi_json (1.3.6) multi_xml (0.5.1) + multipart-post (1.1.5) mysql2 (0.3.11) net-ldap (0.2.2) nokogiri (1.5.3) + oauth (0.4.6) + oauth2 (0.8.0) + faraday (~> 0.8) + httpauth (~> 0.1) + jwt (~> 0.1.4) + multi_json (~> 1.0) + rack (~> 1.2) omniauth (1.1.0) hashie (~> 1.2) rack + omniauth-github (1.0.1) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.0) + omniauth-google-oauth2 (0.1.13) + omniauth (~> 1.0) + omniauth-oauth2 + omniauth-oauth (1.0.1) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.1.0) + oauth2 (~> 0.8.0) + omniauth (~> 1.0) + omniauth-twitter (0.0.12) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) orm_adapter (0.3.0) polyglot (0.3.3) posix-spawn (0.3.6) @@ -411,7 +439,11 @@ DEPENDENCIES minitest (>= 2.10) modernizr (= 2.5.3) mysql2 + omniauth + omniauth-github + omniauth-google-oauth2 omniauth-ldap! + omniauth-twitter pry pygments.rb! rack-mini-profiler diff --git a/app/assets/stylesheets/auth_methods.scss b/app/assets/stylesheets/auth_methods.scss new file mode 100644 index 00000000000..ed6f5b0fe2d --- /dev/null +++ b/app/assets/stylesheets/auth_methods.scss @@ -0,0 +1,10 @@ +.auth_methods { + &ul { + margin: 0; + text-align:center; + padding: 5px; + &li { + display: inline; + } + } +} diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 5613f1e82d8..0e3de4e83fe 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -3,8 +3,8 @@ /** GITLAB colors **/ $text_color:#222; -$lite_text_color: #666; -$link_color:#2A79A3; +$lite_text_color: #666; +$link_color:#2A79A3; $active_link_color:#2FA0BB; $active_bg_color:#79C3E0; $active_bd_color: #2FA0BB; @@ -31,7 +31,7 @@ $hover: #FDF5D9; box-shadow: 0 0 3px #ddd; } -@mixin solid_shade { +@mixin solid_shade { -moz-box-shadow: 0 0 0 3px #eee; -webkit-box-shadow: 0 0 0 3px #eee; box-shadow: 0 0 0 3px #eee; @@ -73,21 +73,21 @@ $hover: #FDF5D9; /** - * Header of application. + * Header of application. * Contain application logo, search panel, profile icon */ @import "sections/header.scss"; /** - * Navigation menu of application. + * Navigation menu of application. * Panel with links to pages depends on project, profile or admin area */ @import "sections/nav.scss"; /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * + * This file represent some UI that can be changed + * during web app restyle or theme select. + * * Next items should be placed there * - link, button colors * - header restyles @@ -118,11 +118,11 @@ $hover: #FDF5D9; * Most of application styles placed here. * This file represent common UI that should not be changed between themes * or project restyling like form width or user avatar class or commit title - * + * * TODO: clean it */ @import "common.scss"; - +@import "auth_methods.scss"; /** * Styles related to specific part of app @@ -140,17 +140,17 @@ $hover: #FDF5D9; @import "ref_select.scss"; /** - * Code (files list) styles. Browsing project files there + * Code (files list) styles. Browsing project files there */ @import "sections/tree.scss"; /** - * This file represent notes(comments) styles + * This file represent notes(comments) styles */ @import "sections/notes.scss"; /** - * Devise styles + * Devise styles */ @import "sections/login.scss"; diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index d19931e93d7..84e578a3865 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -9,7 +9,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController error ||= env["omniauth.error.type"].to_s error.to_s.humanize if error end - + def ldap # We only find ourselves here if the authentication to LDAP was successful. info = request.env["omniauth.auth"]["info"] @@ -20,4 +20,34 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController sign_in_and_redirect @user end + Settings.omniauth_providers.each do |provider| + define_method provider['name'] do + handle_omniauth + end + end + + private + + def handle_omniauth + oauth = request.env['omniauth.auth'] + provider, uid = oauth['provider'], oauth['uid'] + + if current_user + # Change a logged-in user's authentication method: + current_user.uid = uid + current_user.provider = provider + current_user.save + redirect_to profile_path + else + @user = User.find_by_provider_and_uid(provider, uid) + + if @user + sign_in_and_redirect @user + else + flash[:notice] = "There's no such user!" + redirect_to new_user_session_path + end + end + end + end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 51569b0635e..8a457cea70f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -75,16 +75,16 @@ module ApplicationHelper end def show_last_push_widget?(event) - event && + event && event.last_push_to_non_root? && !event.rm_ref? && - event.project && + event.project && event.project.merge_requests_enabled end def tab_class(tab_key) active = case tab_key - + # Project Area when :wall; wall_tab? when :wiki; controller.controller_name == "wikis" @@ -123,4 +123,9 @@ module ApplicationHelper def hexdigest(string) Digest::SHA1.hexdigest string end + + def authbutton(provider, size = 64) + image_tag("authbuttons/#{provider.to_s.split('_').first}_#{size}.png", + alt: "Sign in with #{provider.to_s.titleize}" ) + end end diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index a03838669cf..6b334b87335 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -14,10 +14,15 @@
<%= render :partial => "devise/shared/links" %>
<%- if devise_mapping.omniauthable? %> - <%- resource_class.omniauth_providers.each do |provider| %> -
- <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" %>
- <% end -%> +
+
+ +
<% end -%> <% end %> diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index b624415dfe1..810b346f1cd 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -10,7 +10,7 @@ = link_to "Profile", profile_path %li{class: tab_class(:password)} - = link_to "Password", profile_password_path + = link_to "Authentication", profile_password_path %li{class: tab_class(:ssh_keys)} = link_to keys_path do diff --git a/app/views/profile/password.html.haml b/app/views/profile/password.html.haml index 257dacb1ad3..1d4d468c38b 100644 --- a/app/views/profile/password.html.haml +++ b/app/views/profile/password.html.haml @@ -1,19 +1,31 @@ %h3.page_title Password %hr -= form_for @user, url: profile_password_path, method: :put do |f| - .data - %p.slead After successful password update you will be redirected to login page where you should login with new password - -if @user.errors.any? - .alert-message.block-message.error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg - .clearfix - = f.label :password - .input= f.password_field :password - .clearfix - = f.label :password_confirmation - .input= f.password_field :password_confirmation += form_for @user, url: profile_password_path, method: :put do |f| + .row + .span7 + .data + %p.slead After successful password update you will be redirected to login page where you should login with new password + -if @user.errors.any? + .alert-message.block-message.error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :password + .input= f.password_field :password + .clearfix + = f.label :password_confirmation + .input= f.password_field :password_confirmation + + - if Settings.omniauth.enabled + .span5.right + .auth_methods.alert.alert-info + %strong Tip: Use one of the following sites to login + %ul + - User.omniauth_providers.each do |provider| + %li= link_to authbutton(provider), | + omniauth_authorize_path(User, provider) | .actions = f.submit 'Save', class: "btn primary" diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml index 95cce2bb6b6..4d89cd3dcb3 100644 --- a/app/views/profile/show.html.haml +++ b/app/views/profile/show.html.haml @@ -49,6 +49,13 @@ %strong Tip: You can change your avatar at gravatar.com + - if Settings.omniauth.enabled && @user.provider? + %h4 + Omniauth Providers: + = link_to "Change", profile_password_path, class: "btn small right" + You can login through #{@user.provider.titleize}! + = authbutton(@user.provider, 32) + %h4 Personal projects: %small.right diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1818f2c0d01..622ac9ec560 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -1,4 +1,4 @@ -# # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # Gitlab application config file # # # # # # # # # # # # # # # # # # # @@ -19,14 +19,14 @@ email: # Application specific settings # Like default project limit for user etc -app: - default_projects_limit: 10 +app: + default_projects_limit: 10 # backup_path: "/vol/backups" # default: Rails.root + backups/ # backup_keep_time: 604800 # default: 0 (forever) (in seconds) -# -# 2. Advanced settings: +# +# 2. Advanced settings: # ========================== # Git Hosting configuration @@ -49,3 +49,15 @@ git: git_max_size: 5242880 # 5.megabytes # Git timeout to read commit, in seconds git_timeout: 10 + +# Omniauth configuration +# omniauth: +# enabled: true +# providers: +# - { name: 'google_oauth2', app_id: 'YOUR APP ID', +# app_secret: 'YOUR APP SECRET', +# args: { access_type: 'offline', approval_prompt: '' } } +# - { name: 'twitter', app_id: 'YOUR APP ID', +# app_secret: 'YOUR APP SECRET'} +# - { name: 'github', app_id: 'YOUR APP ID', +# app_secret: 'YOUR APP SECRET' } diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 5c5987a8857..741a29d51be 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -6,7 +6,7 @@ class Settings < Settingslogic self.web['protocol'] ||= web.https ? "https" : "http" end - def web_host + def web_host self.web['host'] ||= 'localhost' end @@ -14,11 +14,11 @@ class Settings < Settingslogic self.email['from'] ||= ("notify@" + web_host) end - def url + def url self['url'] ||= build_url - end + end - def web_port + def web_port if web.https web['port'] = 443 else @@ -36,7 +36,7 @@ class Settings < Settingslogic raw_url << web_host if web_custom_port? - raw_url << ":#{web_port}" + raw_url << ":#{web_port}" end raw_url @@ -111,5 +111,14 @@ class Settings < Settingslogic def backup_keep_time app['backup_keep_time'] || 0 end + + def omniauth_enabled? + omniauth['enabled'] || false + end + + def omniauth_providers + omniauth['providers'] || [] + end + end end diff --git a/db/migrate/20120803152018_add_provider_and_uid_to_users.rb b/db/migrate/20120803152018_add_provider_and_uid_to_users.rb new file mode 100644 index 00000000000..14f53e4ee23 --- /dev/null +++ b/db/migrate/20120803152018_add_provider_and_uid_to_users.rb @@ -0,0 +1,6 @@ +class AddProviderAndUidToUsers < ActiveRecord::Migration + def change + add_column :users, :provider, :string + add_column :users, :uid, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index c4c54f562a3..5ac159d8fac 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120712080407) do +ActiveRecord::Schema.define(:version => 20120803152018) do create_table "events", :force => true do |t| t.string "target_type" @@ -146,31 +146,33 @@ ActiveRecord::Schema.define(:version => 20120712080407) do end create_table "users", :force => true do |t| - t.string "email", :default => "", :null => false - t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "email", :default => "", :null => false + t.string "encrypted_password", :default => "", :null => false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", :default => 0 + t.integer "sign_in_count", :default => 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "name" - t.boolean "admin", :default => false, :null => false - t.integer "projects_limit", :default => 10 - t.string "skype", :default => "", :null => false - t.string "linkedin", :default => "", :null => false - t.string "twitter", :default => "", :null => false + t.boolean "admin", :default => false, :null => false + t.integer "projects_limit", :default => 10 + t.string "skype", :default => "", :null => false + t.string "linkedin", :default => "", :null => false + t.string "twitter", :default => "", :null => false t.string "authentication_token" - t.boolean "dark_scheme", :default => false, :null => false - t.integer "theme_id", :default => 1, :null => false + t.boolean "dark_scheme", :default => false, :null => false + t.integer "theme_id", :default => 1, :null => false t.string "bio" - t.boolean "blocked", :default => false, :null => false - t.integer "failed_attempts", :default => 0 + t.boolean "blocked", :default => false, :null => false + t.integer "failed_attempts", :default => 0 t.datetime "locked_at" + t.string "provider" + t.string "uid" end add_index "users", ["email"], :name => "index_users_on_email", :unique => true diff --git a/vendor/assets/images/authbuttons/github_32.png b/vendor/assets/images/authbuttons/github_32.png new file mode 100644 index 0000000000000000000000000000000000000000..247e52a5f4282110243afa350cd4a13b0154c2a1 GIT binary patch literal 1931 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 literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/google_32.png b/vendor/assets/images/authbuttons/google_32.png new file mode 100644 index 0000000000000000000000000000000000000000..3909e9de93b22e1f3a2e6c264f6289331412a3bb GIT binary patch literal 1615 zcmV-V2C(^wP)8wQ zK~#9!mVSaIA|e!R!IoP5D#joQpiPJxY7C7D!NjNpF-l@8 zi1DY2NsWn#7!t$;1BekdAhv0w)F>aJ(w2_~X`x38y|#DI_If+x?9Sfa?)9z^ewjGw z+jn;Moqe8pKW65D|9c~M`R;BSzax;AmX_Q_ix$<3?zFm(enng70IKHaVfVYs9Cke` zAA{C+1)Oss;zLA2a306vJdRB`f@dSquh(7;T~A41@7}%nwY9Z}RaJfPt*`#VzT>?h zlo%ijW@TZ|rbm(Ka~Zag&UOUWt^j8eRkx7@WZxKvd*yW9_WhmkrF)R(b;Fb9#_+fXkBg#m&U863HxK+q0PNCvC@vLLQG`%X zl}ar{Cr?OBjMe5(qhxsJ@YpdJv;?S)fEtNJX6T)xfA_;HfV~E|OK6wT|L)H|gvO%Q z1mCLXLDRjUUXLY;#|6c$3Q`IbRj~*WDhXZAyl1f;*G>S8F(V{Y7%F0tq7b;mM^s(J z>PMp-EnPP(dv{qSh*&*wxujNOqJZe2x)@70y`X4}pt0W#9!c^hf{&3JXJFde_=d(o4wqQ@uJq-iMa52H9ZiscuE zQ4}1v(QsCNN&;)EXW^$a{kS#~G3Uy9e_~#a@qRFP4X*`TaOmYyjOdlgG-1M|)}m<5 zcLPxk+p2O=bvcAL+xoHOa(EJsBLqLR{$ut1UKO9OE5m(x>DFfRVpTC#mKR`X;ufAd z*@$loRgAbP;&F=YMDbAQe{F?6)U7JUtIG?}m#(DtK*P>I(9|8p$Lp71W{wYq*=g9d zVKMrK#*Ccs3(>)6|ME75BVoB>W7W(Ak3I9a#7K$1BPHoLvbqR3dp$V;JzHBl2C!^v z6Us#;l!(fJ}G<232uotwFht;59nK^Zpbty!A*Ve zM4w~Q#>NoBUKp|=XkMUbty_qvQafRC*|Q@JF^l8T${1% z@OvhZCbeewj6L?r88|%f<>nf+?RXBaJUs_X3eJG_ZUnnf19plS!UJ0!lWX!9;{Am$ z#KB|LVD|#&PA?>EL=FNK_s;;24uPF62k#OWkp3M&s5xHdSGR#(tTk+`a4dK>zH&n} z8@SyQ$uC9@0v>w9&)Nk*__RE&xp`{GM1Kx`4}Px3nv?C$K#6ZgLj3Lh)b5`|AT#vE z0~lEODrljcep(blubI%E90w(4F z=Sfb?WM!DQMx2)=GTzJvm`Fkh@s%5MntD&#Vy`*FkhmAzZXYlP9y%Z{d|C1$shJW@ z=o9h2XroNR=Fd!EbHy4@vI*G$GCV~o35<@@E5u(8T5$>_f1T3b&R&Z~N{9=iDztaY8;@6P`C#`qK7Vn6RW!jS8~bu` z_shnm;j5@Wv|YyDsw^JwC%QZKJf>YFd%Ejsceq`Q+!ANbguE_N;-Zv#gkjbr)z$MG z8XDG=m6cTs17er%Yhm)uiB__!{YN4{N^G@F%wHh!y5WRa{aZsPkJF=peYAJ9Q`ih~ z#>N@v5t}QFrV1dx81#z)HzmX+#uRnGZX zkdUy9GS1i?{Wdgc8UI10f^p3y33JVRtGu{z`8!FHaU2Byz4osF0|3bfn|->$iJkxe N002ovPDHLkV1hOX`1Sw* literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/google_64.png b/vendor/assets/images/authbuttons/google_64.png new file mode 100644 index 0000000000000000000000000000000000000000..e55f34f1b7d57c52172eb2bab0964037c532629b GIT binary patch literal 3446 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 Y0PP3DX~$FU?EnA(07*qoM6N<$g6|lC761SM literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/twitter_32.png b/vendor/assets/images/authbuttons/twitter_32.png new file mode 100644 index 0000000000000000000000000000000000000000..daadcffd315f11026cface9d99409997050ab9b9 GIT binary patch literal 1439 zcmV;Q1z`G#P)Y_}bdkmx`I#UBDP81NhfpbG&U zJv0JOj1Iv3@;aQIy#*$Uop&g^WHcK^3)l?_0n+>QjH3I@o%-Uft*pVYm8R%O=g{zn z0tH7WN8sh@gR(X;)Gzawmu}LBjU%Pwxbn1egKk(bDkWR-p@I8Zo4N&n!Iqp6I68o- z(LF8OG%?&yz_fEyWzectT4Tzdlt;QyY~=2MmN+A{z);<@S5asvP3SBEWi%)vr!qE0 z1qd@=r5JKlpbY@C6)>C2wASU_J(VIHo7xYU*c9Uj4Ei(#xUswj=l{G5Ks*tL5VJAL z2cWb{4g0z2Lh~4`6yO-~VyB}N2Jp>qHJ@#{+tA7Zr#Iq>pAmPZyU$V9*bp>#6T4v0 z?Js)evLe%U1A02HEUZ$9vc|D155jKsB9&DE11^TmV>3$Nc74-1a_zGthtKUw2!hx+Pxzyn=8Z<&^3vYEqL7qF zl6r5!rggy^cBu_%D(*x=%*0;2vDNJAV!Z>v>(4v}mHrB8Yk_jE0IHZ7Bv7DL%6x;S zm5li$giKYl+o@s&7i!CJ{K-dKo_ls|SVp(wySY1X^4s5Gb#)Ce_PWwNTRL@scObF# zoWl@NEbp$Y!`E{QJ*VIckMD=qj!fDT?nSM`_Z%JYArLk*p(q6Lb_AdPG7mRa{_YvX z_;587dd$s)P9Z{{PKhu)Cbl=W8}NX3;l!!)aQb4cCje(IT(=p5rlqCZISVo*z` zYWF7Pv?4H{{P+iae(^dS9@_`Q)pDlfLh9u;BJH`$H>qifQXRmYg4jVq^yy@KIO$}h z>EK3%3k#vz-q?b(OAn;F`D)h$Z&T0E`&=#2wQW5KQ4&Y>l!RcLQI#~2x65gJjM1mE z6E7>nUZkCksIi`{%Ym|uNJuJJ-dtV0KXmA+7j+Qs^_9?Mgvpr1HlqGoJWSk-X?yOw z=63auw>JK|{MkqEH|k3()VPS2N!LbwK!HG%=&gZ=4^B>vKmW?B)uGYJa)0#^>BR{# z%FKN^M*~$3skkL_;Z|rjkshYCd$p@S&Hr%v)Y|Q9S7?2eoY*D@8|hXkdxT`DN{dxm zDAKMJXf5D%H}lZ8Y{;y#{FO~dltiMLN9^W^Zb$SSOX?bv$%sbx@V}wJl3z+$Wjj8w tU9M`YBd+9ILRkF~K+PWazj*v3zyKvCC{2!YO3?rS002ovPDHLkV1fr@ymbHo literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/twitter_64.png b/vendor/assets/images/authbuttons/twitter_64.png new file mode 100644 index 0000000000000000000000000000000000000000..68b74530c06b558b6c4848c2637d4caeaf4c969b GIT binary patch literal 3384 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 O0000 Date: Fri, 17 Aug 2012 16:32:22 +0200 Subject: [PATCH 2/6] Adding default values for omniauth settings so tests don't failt --- config/gitlab.yml.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 622ac9ec560..85149fe890c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -51,6 +51,10 @@ git: git_timeout: 10 # Omniauth configuration +omniauth: + enabled: false + providers: + # omniauth: # enabled: true # providers: From 36ffdf36b96a877154f265327d83d022ed27e9e4 Mon Sep 17 00:00:00 2001 From: Florian Unglaub Date: Fri, 24 Aug 2012 15:40:44 +0200 Subject: [PATCH 3/6] Merge issue fixed --- app/controllers/omniauth_callbacks_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 9b40e5640d0..00ec7c42603 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -33,7 +33,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController if current_user # Change a logged-in user's authentication method: - current_user.uid = uid + current_user.extern_uid = uid current_user.provider = provider current_user.save redirect_to profile_path From 6d6c7a17ea2d2a61d4f251d6d746ebe9438405ca Mon Sep 17 00:00:00 2001 From: Florian Unglaub Date: Fri, 31 Aug 2012 15:45:50 +0200 Subject: [PATCH 4/6] Allow single-sign-on with Omniauth --- .../omniauth_callbacks_controller.rb | 4 +-- app/models/user.rb | 34 ++++++++++++++++++- config/gitlab.yml.example | 2 ++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 00ec7c42603..248a75a88cb 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -38,7 +38,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController current_user.save redirect_to profile_path else - @user = User.find_by_provider_and_extern_uid(provider, uid) + @user = User.find_or_new_for_omniauth(oauth) + @user.save! if @user.try('new_record?') if @user sign_in_and_redirect @user @@ -48,5 +49,4 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end end end - end diff --git a/app/models/user.rb b/app/models/user.rb index ad6af6a6dd0..b956d4ed433 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,6 +86,39 @@ class User < ActiveRecord::Base where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') end + def self.find_or_new_for_omniauth(oauth) + provider, uid = oauth['provider'], oauth['uid'] + + if @user = User.find_by_provider_and_extern_uid(provider, uid) + @user + else + if Gitlab.config.omniauth.allow_single_sign_on + # Ensure here that all required attributes were passed along with the + # oauth request: + %w(first_name last_name email).each do |attr| + unless oauth[:info][attr].present? + raise OmniAuth::Error, + "#{provider} does not provide the required field #{attr}" + end + end + + password = Devise.friendly_token[0, 8].downcase + @user = User.new( + extern_uid: uid, + provider: provider, + name: "#{oauth[:info][:first_name]} #{oauth[:info][:last_name]}", + email: oauth[:info][:email], + password: password, + password_confirmation: password, + projects_limit: Gitlab.config.default_projects_limit, + ) + + @user.blocked = true if Gitlab.config.omniauth.block_auto_created_users + @user + end + end + end + def self.find_for_ldap_auth(auth, signed_in_resource=nil) uid = auth.info.uid provider = auth.provider @@ -148,4 +181,3 @@ end # bio :string(255) # blocked :boolean(1) default(FALSE), not null # - diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1934029d5bb..b5aae4971ed 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -53,6 +53,8 @@ git: omniauth: enabled: false providers: + allow_single_sign_on: false + block_auto_created_users: true # omniauth: # enabled: true From 1b0198f1d3fc621b339af0e7fd79a74919856d46 Mon Sep 17 00:00:00 2001 From: Florian Unglaub Date: Fri, 31 Aug 2012 16:24:12 +0200 Subject: [PATCH 5/6] save newly created users directly in the model --- app/controllers/omniauth_callbacks_controller.rb | 1 - app/models/user.rb | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 248a75a88cb..3be285ba1f6 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -39,7 +39,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_to profile_path else @user = User.find_or_new_for_omniauth(oauth) - @user.save! if @user.try('new_record?') if @user sign_in_and_redirect @user diff --git a/app/models/user.rb b/app/models/user.rb index b956d4ed433..0d45b6e537a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -114,6 +114,8 @@ class User < ActiveRecord::Base ) @user.blocked = true if Gitlab.config.omniauth.block_auto_created_users + @user.save! + @user end end From 0dd94cd86ec0680432e58f2630a3a35fa84afd73 Mon Sep 17 00:00:00 2001 From: Florian Unglaub Date: Fri, 31 Aug 2012 16:44:23 +0200 Subject: [PATCH 6/6] DRY'ed up the user model --- app/models/user.rb | 69 +++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 0d45b6e537a..fa5d6834849 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,36 +86,42 @@ class User < ActiveRecord::Base where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') end - def self.find_or_new_for_omniauth(oauth) - provider, uid = oauth['provider'], oauth['uid'] + def self.create_from_omniauth(auth, ldap = false) + provider, uid = auth.provider, auth.uid + name = auth.info.name.force_encoding("utf-8") + email = auth.info.email.downcase unless auth.info.email.nil? + + ldap_prefix = ldap ? '(LDAP) ' : '' + raise OmniAuth::Error, "#{ldap_prefix}#{provider} does not provide an email"\ + " address" if auth.info.email.blank? + + logger.info "#{ldap_prefix}Creating user from #{provider} login"\ + " {uid => #{uid}, name => #{name}, email => #{email}}" + password = Devise.friendly_token[0, 8].downcase + @user = User.new( + extern_uid: uid, + provider: provider, + name: name, + email: email, + password: password, + password_confirmation: password, + projects_limit: Gitlab.config.default_projects_limit, + ) + if Gitlab.config.omniauth.block_auto_created_users && !ldap + @user.blocked = true + end + @user.save! + @user + end + + def self.find_or_new_for_omniauth(auth) + provider, uid = auth.provider, auth.uid if @user = User.find_by_provider_and_extern_uid(provider, uid) @user else if Gitlab.config.omniauth.allow_single_sign_on - # Ensure here that all required attributes were passed along with the - # oauth request: - %w(first_name last_name email).each do |attr| - unless oauth[:info][attr].present? - raise OmniAuth::Error, - "#{provider} does not provide the required field #{attr}" - end - end - - password = Devise.friendly_token[0, 8].downcase - @user = User.new( - extern_uid: uid, - provider: provider, - name: "#{oauth[:info][:first_name]} #{oauth[:info][:last_name]}", - email: oauth[:info][:email], - password: password, - password_confirmation: password, - projects_limit: Gitlab.config.default_projects_limit, - ) - - @user.blocked = true if Gitlab.config.omniauth.block_auto_created_users - @user.save! - + @user = User.create_from_omniauth(auth) @user end end @@ -124,7 +130,6 @@ class User < ActiveRecord::Base def self.find_for_ldap_auth(auth, signed_in_resource=nil) uid = auth.info.uid provider = auth.provider - name = auth.info.name.force_encoding("utf-8") email = auth.info.email.downcase unless auth.info.email.nil? raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil? @@ -136,17 +141,7 @@ class User < ActiveRecord::Base @user.update_attributes(:extern_uid => uid, :provider => provider) @user else - logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}" - password = Devise.friendly_token[0, 8].downcase - @user = User.create( - :extern_uid => uid, - :provider => provider, - :name => name, - :email => email, - :password => password, - :password_confirmation => password, - :projects_limit => Gitlab.config.default_projects_limit - ) + create_from_omniauth(auth) end end