From a64aff2f1c1ddc77b00211489d74fbc23c0c2fa2 Mon Sep 17 00:00:00 2001 From: Florian Unglaub Date: Fri, 3 Aug 2012 17:27:39 +0200 Subject: [PATCH 1/9] 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 d2a5728f..12610b65 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 7356c35e..b34f401b 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 00000000..ed6f5b0f --- /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 5613f1e8..0e3de4e8 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 d19931e9..84e578a3 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 51569b06..8a457cea 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 a0383866..6b334b87 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 b624415d..810b346f 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 257dacb1..1d4d468c 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 95cce2bb..4d89cd3d 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 1818f2c0..622ac9ec 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 5c5987a8..741a29d5 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 00000000..14f53e4e --- /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 c4c54f56..5ac159d8 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 zcmV-84af3{P)|0%I9Y+;DGrRXDwv)KA;{-zf6jEA3lT@ul+6P4XQUxkNMSZP=5E3suAS&^I z-~|vr;sNoFgoIRy2zUXC3aBbks-iZeDp5t#Bsfvh#LZ9b*iP)&zB`BcIWu!+@4eo= zH+DcuR+_tiv%7P?bLRZc&EOv2vikts2jD&c_w*)u>??YF=6|svYbyePBuQz|UjCmG zuXJ0|t)Pbe0$}dtx;6Jb`;7<3_B{ISH3LJRN5o+v1PCeI!$O44`kwt-xK8J0Z=Nyt z>6C8!>w46v+DJh^c6updTZe>lJEt=_T`VoWGZ)05}l{nCjSe{@`*-R=g0SVrsz4t()zk8IiZ*}pUzjp0Tk2fc^9k%0jsvmglj z1|th`&?F52>EIYZj`I+X+nakW+f9Ln$^o{WoPyO@AFHY$0l6kVlNtc&Fbo7+LOmRUIA=!ZrvnBtR!R{_QBqL3fve8M zfW^VzH_!9F{8@cZIc+e!e*50<=%cUe)~XADAn_l}*9<&u2sYd|#5eUm&t(LFiN6(T z_Y;61QZ=zm+?AIGnTW0`O#yq&5*fLxJkjPIO4w(FB7%i`chEcfSP2DrbMR@Rr=#-j z27r-(rVw&i(!94oSmM0|2rU6l2YA&dXi6ZPS?GyYoMIGN}N~Hf*_@` z_YCnu6N!cq1_V$vWz1OIG}B!+FyckoDs%(D>HzZs!|99&Hph7;!np&$JfOSlV+__o zb63!$P)>QN!f_7^X-Es=96645;2aDooJdP64QPon-?YSQ!X1&qaykl}ezzF|l7e>h zHoIy1d>Jb+0-hitBeD8NA6oI3bkAD@jfy?iKd@_boqfr~^c>7Bv~=G>6R+%Gt@sKL zg4Y9%Yi>G_f>e$lR>Gv4!OB`#Nn?p1_y%mxbo_{JAM^@9bp3=^bt1WGc`Ym&AdFRz z89cFj3=Zwt0&5%5%YOCD6?o;n%Wi52r_}`-sc{r}1!}nFNv01#m|*C~0f7;r(WDF4 z`ACJR^zyoGliZ)gZr)`WjI_ib{^Zt#c(XnA;1+o1k!>JyH&rZPz50{Ox@;mg)>*_E z|3NZG_=+PHxu@Sc(9m$5|B;EYAl* z6cbMlN+>&y?iyX+#`*HLacC{+i1z8!sUZPe8L}Ys693fK0oI4-9}%1h7$&4Qf0dDn z1xu3ha_7Q3N{JiAK1#1fFW%uhs*B`cPcY8@R69me|U`r?131e6Y#UoZOks8xQ(RAzvgGh@<6iN?~AD=P_H zC7q{7j)3zwtl1#O2m!dP0>R4Zlh+BcF^R~+>=c}f>H!mO2gi*ZJ?QZOBtjw(a;@ohJ7i)@Lq-hPgsPM*WILE zZ`Wt%L-M07501EUO3*tU9Ej)uK@45*A_i|=oP?`0^De)jd=>~&FnX{EvdR^X$xSR} zaO*j0>K6R+wNs#$S}wPMD&!pzO3JXNHK=rjfjK}C1W2;CSZ}()iOV-&a&D1X zlGL;&sc=Zs;@4(x!?Dv7P%JJ1whEgE1j;f2&jltH2`fXC%3z3_KCb){5Xe$gZO7lA zg0oj=ocLN1R+J2lX)*s^#j?PKa{UWUxGD;IzG$l()YN-DC_!%eSX$c-kkCa6TI$RI zORy2$B~3)D6|%hLT0jDV0id8XxP4v|-cl?Tu9)eI`k<~}u zrW&+F6^;mHxTzG&7DwV0#)^-!2~{H0^fk{1$zjtF%0y!=iKsM#QQODCNkTwTm@0e# zQ?YGa+a%5H2vEe;BtzU*3~|J(Qqcs6DP@ne*O;Kv3AeW}*DnGXGBg2#wl`bh_vOm(Nz9c19 zJn+obWoho8 zKX~Zlo8kFS?}ovFH6R*!Y`Jbt*C1SP*e2<|0mXu0BnVjIv=o(N55Z7nMu^W(F99}d zqnwQ}n@xZ&1MfL`{pM}>_vI=0`>9K|K!P?QqC(Bk zkC7>b70L-FDR-uk(G?>t`c?v5e(&uIlknmG1sK&KYn|;AW9w@Ua~M9eXA2zu)HeA3 zA5Or`?416tNr)&i)uKc8s-7bt>o`KZHXkLy*=REb3S*nKT{oWEuaA$fS}d@0bRE2K zc)!yTRA1=mmg5TP0G#2zqCI0H{Jd9tN4$EgkY8<=NbneUwL{vTZ1;#3k07aEvsu3D< zU~2!yL~6w-@&aCb{WKgqGXc*&x)UDTK6+=I*Ur{Y|NOR|YAzvSLt^d@m)91qj32;i zfQIwJHCwH*?kbiNDBvo=X z6^^`d7RDzgt)yAvLqMcT>kC)V5sG0cP}E0Gh%M8Jv?z+SV99)0bI6-2Vd`1hdz)BT zgqN<*z@NyMZC^+k{d(oSP(^D5Y04oHZ_F+ajQTYMH4-8vZ_uvBa^jz^%81Z=(shDxn~LLk?UTK>4|eiv9$Qk$gaJI3V>Do z({vSI7KRBXz5WGGw3kl< zNhCae<&NWaIELW=EcfknX)=?9Fg4;aFbP5EFBrVP-T&+Cy$-;C1sDK!FMbnQfbweq O0000 Date: Fri, 17 Aug 2012 16:32:22 +0200 Subject: [PATCH 2/9] 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 622ac9ec..85149fe8 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/9] 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 9b40e564..00ec7c42 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/9] 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 00ec7c42..248a75a8 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 ad6af6a6..b956d4ed 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 1934029d..b5aae497 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/9] 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 248a75a8..3be285ba 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 b956d4ed..0d45b6e5 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/9] 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 0d45b6e5..fa5d6834 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 From 486de8c3f412df3e71c9045faf250941c03c8c00 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 12 Sep 2012 07:48:22 +0300 Subject: [PATCH 7/9] Refactoring auth --- Gemfile.lock | 32 +++++++++++++++ app/assets/stylesheets/auth_methods.scss | 4 +- app/views/devise/sessions/new.html.erb | 32 --------------- config/gitlab.yml.example | 50 +++++++++++++++--------- config/initializers/1_settings.rb | 10 ++++- config/initializers/devise.rb | 17 ++++++++ config/initializers/omniauth.rb.sample | 15 ------- 7 files changed, 91 insertions(+), 69 deletions(-) delete mode 100644 app/views/devise/sessions/new.html.erb delete mode 100644 config/initializers/omniauth.rb.sample diff --git a/Gemfile.lock b/Gemfile.lock index 8046b92b..3d27d3fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -158,6 +158,8 @@ GEM factory_girl_rails (4.0.0) factory_girl (~> 4.0.0) railties (>= 3.0.0) + faraday (0.8.4) + multipart-post (~> 1.1) ffaker (1.14.0) ffi (1.0.11) foreman (0.47.0) @@ -194,6 +196,7 @@ GEM httparty (0.8.3) multi_json (~> 1.0) multi_xml + httpauth (0.1) i18n (0.6.1) journey (1.0.4) jquery-rails (2.0.2) @@ -203,6 +206,8 @@ GEM jquery-rails railties (>= 3.1.0) json (1.7.5) + jwt (0.1.5) + multi_json (>= 1.0) kaminari (0.14.0) actionpack (>= 3.0.0) activesupport (>= 3.0.0) @@ -225,12 +230,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.7) + 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.3) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + 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.13) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) orm_adapter (0.3.0) polyglot (0.3.3) posix-spawn (0.3.6) @@ -420,7 +448,11 @@ DEPENDENCIES linguist (~> 1.0.0)! 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 index ed6f5b0f..f94a0e52 100644 --- a/app/assets/stylesheets/auth_methods.scss +++ b/app/assets/stylesheets/auth_methods.scss @@ -1,9 +1,9 @@ .auth_methods { - &ul { + ul { margin: 0; text-align:center; padding: 5px; - &li { + li { display: inline; } } diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb deleted file mode 100644 index 6b334b87..00000000 --- a/app/views/devise/sessions/new.html.erb +++ /dev/null @@ -1,32 +0,0 @@ -<% unless ldap_enable? -%> - - <%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| %> - <%= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" %> - - <%= f.text_field :email, :class => "text top", :placeholder => "Email" %> - <%= f.password_field :password, :class => "text bottom", :placeholder => "Password" %> - - <% if devise_mapping.rememberable? -%> -
- <% end -%> -
- <%= f.submit "Sign in", :class => "primary btn" %> -
<%= render :partial => "devise/shared/links" %>
- - <%- if devise_mapping.omniauthable? %> -
-
-
    - <%- resource_class.omniauth_providers.each do |provider| %> -
  • <%= link_to authbutton(provider), - omniauth_authorize_path(resource_name, provider) %>
  • - <% end -%> -
-
- <% end -%> - - <% end %> - -<% else %> - <%= render :partial => 'devise/sessions/new_ldap' %> -<% end %> diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 809d7ee9..a52bc1b9 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -25,8 +25,38 @@ app: # backup_keep_time: 604800 # default: 0 (forever) (in seconds) # disable_gravatar: true # default: false - Disable user avatars from Gravatar.com + + + # -# 2. Advanced settings: +# 2. Auth settings +# ========================== +ldap: + enabled: false + host: '_your_ldap_server' + base: '_the_base_where_you_search_for_users' + port: 636 + uid: 'sAMAccountName' + method: 'ssl' # plain + bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' + password: '_the_password_of_the_bind_user' + +omniauth: + enabled: false + allow_single_sign_on: false + block_auto_created_users: 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' } + + +# +# 3. Advanced settings: # ========================== # Git Hosting configuration @@ -50,21 +80,3 @@ git: git_max_size: 5242880 # 5.megabytes # Git timeout to read commit, in seconds git_timeout: 10 - -# Omniauth configuration -omniauth: - enabled: false - providers: - allow_single_sign_on: false - block_auto_created_users: true - -# 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 00b7cc09..326f5af2 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -120,8 +120,16 @@ class Settings < Settingslogic app['backup_keep_time'] || 0 end + def ldap_enabled? + ldap['enabled'] + rescue + false + end + def omniauth_enabled? - omniauth['enabled'] || false + omniauth && omniauth['enabled'] + rescue + false end def omniauth_providers diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 54011ba5..8f3cef5a 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -204,4 +204,21 @@ Devise.setup do |config| # manager.intercept_401 = false # manager.default_strategies(:scope => :user).unshift :some_external_strategy # end + + gl = Gitlab.config + + if gl.ldap_enabled? + config.omniauth :ldap, + :host => gl.ldap['host'], + :base => gl.ldap['base'], + :uid => gl.ldap['uid'], + :port => gl.ldap['port'], + :method => gl.ldap['method'], + :bind_dn => gl.ldap['bind_dn'], + :password => gl.ldap['password'] + end + + gl.omniauth_providers.each do |gl_provider| + config.omniauth gl_provider['name'].to_sym, gl_provider['app_id'], gl_provider['app_secret'] + end end diff --git a/config/initializers/omniauth.rb.sample b/config/initializers/omniauth.rb.sample deleted file mode 100644 index 6e844efd..00000000 --- a/config/initializers/omniauth.rb.sample +++ /dev/null @@ -1,15 +0,0 @@ -# Copy this file to 'omniauth.rb' and configure it as necessary. -# The wiki has further details on configuring each provider. - -Devise.setup do |config| - # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' - - # config.omniauth :ldap, - # :host => 'YOUR_LDAP_SERVER', - # :base => 'THE_BASE_WHERE_YOU_SEARCH_FOR_USERS', - # :uid => 'sAMAccountName', - # :port => 389, - # :method => :plain, - # :bind_dn => 'THE_FULL_DN_OF_THE_USER_YOU_WILL_BIND_WITH', - # :password => 'THE_PASSWORD_OF_THE_BIND_USER' -end From fa4150d47d88b85d6027729844480a3e7c71d3cd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 12 Sep 2012 08:23:20 +0300 Subject: [PATCH 8/9] Cleanup after omniauth --- app/assets/stylesheets/auth_methods.scss | 10 ----- .../stylesheets/gitlab_bootstrap/blocks.scss | 4 ++ app/assets/stylesheets/main.scss | 1 - .../omniauth_callbacks_controller.rb | 11 +++-- app/helpers/application_helper.rb | 5 ++- app/views/devise/sessions/new.html.haml | 9 ++-- app/views/profile/password.html.haml | 6 +-- app/views/profile/show.html.haml | 44 +++++++++++-------- config/gitlab.yml.example | 6 ++- 9 files changed, 49 insertions(+), 47 deletions(-) delete mode 100644 app/assets/stylesheets/auth_methods.scss diff --git a/app/assets/stylesheets/auth_methods.scss b/app/assets/stylesheets/auth_methods.scss deleted file mode 100644 index f94a0e52..00000000 --- a/app/assets/stylesheets/auth_methods.scss +++ /dev/null @@ -1,10 +0,0 @@ -.auth_methods { - ul { - margin: 0; - text-align:center; - padding: 5px; - li { - display: inline; - } - } -} diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss index 70f7889f..e0ae8db7 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss @@ -142,4 +142,8 @@ border:none; } } + + .ui-box-body { + padding:10px; + } } diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 201c69f4..9a6d4456 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -134,7 +134,6 @@ $hover: #fdf5d9; * TODO: clean it */ @import "common.scss"; -@import "auth_methods.scss"; /** * Styles related to specific part of app diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 3be285ba..2fb783b2 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,4 +1,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController + Gitlab.config.omniauth_providers.each do |provider| + define_method provider['name'] do + handle_omniauth + end + end # Extend the standard message generation to accept our custom exception def failure_message @@ -19,12 +24,6 @@ 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 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7033daf8..fb1393e2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -137,7 +137,8 @@ module ApplicationHelper end def authbutton(provider, size = 64) - image_tag("authbuttons/#{provider.to_s.split('_').first}_#{size}.png", - alt: "Sign in with #{provider.to_s.titleize}" ) + file_name = "#{provider.to_s.split('_').first}_#{size}.png" + image_tag("authbuttons/#{file_name}", + alt: "Sign in with #{provider.to_s.titleize}") end end diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 6e86186c..e217cba6 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -15,7 +15,8 @@ .right = render :partial => "devise/shared/links" - if devise_mapping.omniauthable? - - resource_class.omniauth_providers.each do |provider| - %hr/ - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" - %br/ + %hr/ + %ul.unstyled + - resource_class.omniauth_providers.each do |provider| + %li + = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) diff --git a/app/views/profile/password.html.haml b/app/views/profile/password.html.haml index 2405b9d7..bf58e2ae 100644 --- a/app/views/profile/password.html.haml +++ b/app/views/profile/password.html.haml @@ -19,11 +19,11 @@ = f.label :password_confirmation .input= f.password_field :password_confirmation - - if Settings.omniauth.enabled + - if Gitlab.config.omniauth_enabled? .span5.right - .auth_methods.alert.alert-info + .alert.alert-info %strong Tip: Use one of the following sites to login - %ul + %ul.unstyled - User.omniauth_providers.each do |provider| %li= link_to authbutton(provider), | omniauth_authorize_path(User, provider) | diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml index 1e53ead6..8369da4c 100644 --- a/app/views/profile/show.html.haml +++ b/app/views/profile/show.html.haml @@ -50,28 +50,34 @@ %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) + - @user.provider = 'twitter' + - if Gitlab.config.omniauth_enabled? && @user.provider? + .ui-box + .ui-box-body + %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 - %span= current_user.my_own_projects.count - of - %span= current_user.projects_limit - .progress - .bar{style: "width: #{current_user.projects_limit_percent}%;"} + .ui-box + .ui-box-body + %h4 + Personal projects: + %small.right + %span= current_user.my_own_projects.count + of + %span= current_user.projects_limit + .progress + .bar{style: "width: #{current_user.projects_limit_percent}%;"} - %h4 - SSH public keys: - %small.right - %span= link_to current_user.keys.count, keys_path + .ui-box + .ui-box-body + %h4 + SSH public keys: + %strong.right= link_to current_user.keys.count, keys_path - = link_to "Add Public Key", new_key_path, class: "btn small right" + = link_to "Add Public Key", new_key_path, class: "btn small" .form-actions = f.submit 'Save', class: "btn save-btn" diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index a52bc1b9..3e4668ce 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -43,8 +43,6 @@ ldap: omniauth: enabled: false - allow_single_sign_on: false - block_auto_created_users: true providers: # - { name: 'google_oauth2', app_id: 'YOUR APP ID', # app_secret: 'YOUR APP SECRET', @@ -53,6 +51,10 @@ omniauth: # app_secret: 'YOUR APP SECRET'} # - { name: 'github', app_id: 'YOUR APP ID', # app_secret: 'YOUR APP SECRET' } + # IMPORTANT! + # It allows user to login without having user account + allow_single_sign_on: false + block_auto_created_users: true # From 048d47e6266b5b078a169f1657d07883e86f169b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 12 Sep 2012 09:23:16 +0300 Subject: [PATCH 9/9] Refactorn oauth & ldap --- app/models/user.rb | 57 +++--------------------- config/gitlab.yml.example | 15 ++++--- lib/gitlab/auth.rb | 66 +++++++++++++++++++++++++++ spec/lib/auth_spec.rb | 93 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 55 deletions(-) create mode 100644 lib/gitlab/auth.rb create mode 100644 spec/lib/auth_spec.rb diff --git a/app/models/user.rb b/app/models/user.rb index fa5d6834..47876722 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -87,62 +87,19 @@ class User < ActiveRecord::Base end 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 + gitlab_auth.create_from_omniauth(auth, ldap) 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 - @user = User.create_from_omniauth(auth) - @user - end - end + gitlab_auth.find_or_new_for_omniauth(auth) end - def self.find_for_ldap_auth(auth, signed_in_resource=nil) - uid = auth.info.uid - provider = auth.provider - 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? + def self.find_for_ldap_auth(auth, signed_in_resource = nil) + gitlab_auth.find_for_ldap_auth(auth, signed_in_resource) + end - if @user = User.find_by_extern_uid_and_provider(uid, provider) - @user - # workaround for backward compatibility - elsif @user = User.find_by_email(email) - logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}" - @user.update_attributes(:extern_uid => uid, :provider => provider) - @user - else - create_from_omniauth(auth) - end + def self.gitlab_auth + Gitlab::Auth.new end def self.search query diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 3e4668ce..80d95b26 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -42,7 +42,16 @@ ldap: password: '_the_password_of_the_bind_user' omniauth: - enabled: false + # Enable ability for users + # to login via twitter, google .. + enabled: true + + # IMPORTANT! + # It allows user to login without having user account + allow_single_sign_on: false + block_auto_created_users: true + + # Auth providers providers: # - { name: 'google_oauth2', app_id: 'YOUR APP ID', # app_secret: 'YOUR APP SECRET', @@ -51,10 +60,6 @@ omniauth: # app_secret: 'YOUR APP SECRET'} # - { name: 'github', app_id: 'YOUR APP ID', # app_secret: 'YOUR APP SECRET' } - # IMPORTANT! - # It allows user to login without having user account - allow_single_sign_on: false - block_auto_created_users: true # diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb new file mode 100644 index 00000000..ef058ff5 --- /dev/null +++ b/lib/gitlab/auth.rb @@ -0,0 +1,66 @@ +module Gitlab + class Auth + def find_for_ldap_auth(auth, signed_in_resource = nil) + uid = auth.info.uid + provider = auth.provider + 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? + + if @user = User.find_by_extern_uid_and_provider(uid, provider) + @user + elsif @user = User.find_by_email(email) + log.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}" + @user.update_attributes(:extern_uid => uid, :provider => provider) + @user + else + create_from_omniauth(auth, true) + end + end + + def create_from_omniauth auth, ldap = false + provider = auth.provider + uid = auth.info.uid || 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? + + log.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 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 + @user = create_from_omniauth(auth) + @user + end + end + end + + def log + Gitlab::AppLogger + end + end +end diff --git a/spec/lib/auth_spec.rb b/spec/lib/auth_spec.rb new file mode 100644 index 00000000..5faf1307 --- /dev/null +++ b/spec/lib/auth_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe Gitlab::Auth do + let(:gl_auth) { Gitlab::Auth.new } + + before do + @info = mock( + uid: '12djsak321', + name: 'John', + email: 'john@mail.com' + ) + end + + describe :find_for_ldap_auth do + before do + @auth = mock( + uid: '12djsak321', + info: @info, + provider: 'ldap' + ) + end + + it "should find by uid & provider" do + User.should_receive :find_by_extern_uid_and_provider + gl_auth.find_for_ldap_auth(@auth) + end + + it "should update credentials by email if missing uid" do + user = double('User') + User.stub find_by_extern_uid_and_provider: nil + User.stub find_by_email: user + user.should_receive :update_attributes + gl_auth.find_for_ldap_auth(@auth) + end + + + it "should create from auth if user doesnot exist"do + User.stub find_by_extern_uid_and_provider: nil + User.stub find_by_email: nil + gl_auth.should_receive :create_from_omniauth + gl_auth.find_for_ldap_auth(@auth) + end + end + + describe :find_or_new_for_omniauth do + before do + @auth = mock( + info: @info, + provider: 'twitter', + uid: '12djsak321', + ) + end + + it "should find user"do + User.should_receive :find_by_provider_and_extern_uid + gl_auth.should_not_receive :create_from_omniauth + gl_auth.find_or_new_for_omniauth(@auth) + end + + it "should not create user"do + User.stub find_by_provider_and_extern_uid: nil + gl_auth.should_not_receive :create_from_omniauth + gl_auth.find_or_new_for_omniauth(@auth) + end + + it "should create user if single_sing_on"do + Gitlab.config.omniauth.stub allow_single_sign_on: true + User.stub find_by_provider_and_extern_uid: nil + gl_auth.should_receive :create_from_omniauth + gl_auth.find_or_new_for_omniauth(@auth) + end + end + + describe :create_from_omniauth do + it "should create user from LDAP" do + @auth = mock(info: @info, provider: 'ldap') + user = gl_auth.create_from_omniauth(@auth, true) + + user.should be_valid + user.extern_uid.should == @info.uid + user.provider.should == 'ldap' + end + + it "should create user from Omniauth" do + @auth = mock(info: @info, provider: 'twitter') + user = gl_auth.create_from_omniauth(@auth, false) + + user.should be_valid + user.extern_uid.should == @info.uid + user.provider.should == 'twitter' + end + end +end