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 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9FrogmDZn=$tn z0|SF(iEBhjaDG}zd16s2gJVj5QmTSyZen_BP-IyNBZ6tSKNPpV|m%E z(z`#JkEHLld%ov>`Mvi)@BL={cfb9MP+7nVviY$L^wP|O}OTS%lo7SGQkL~Zj_on*i)W!Dmb=iWM9(>qr|IxJd z_U^aaKK))3Cd#*)x8Lpx>*`ekBfg{>@u=+>!{M<_v?hXv+hil zm^aTka;9iqKet`gw-C!CmO5hGA+9GUvv%b)`n*3SG&yBX$h2&k&~-;zgY)J9kN9PFf$I7Pq}(VWoM3@kTWhmFYskN0&{%zI(~SHGk~??vyc82@BgeDM~wX z*R-=rM)Km<552f^&nYdq^^`7)Q<^~0L`ky_?aJB(FI)wKV_zN+-@Y~B_cDgIoNmJSutbOpEGTbcCg+AUv95EpO=u3vC}eYtj946stek z@>~C8?CuGUxHqdKsaiwfhrR75out*-FRETJNSSS);qay3lTVdlUv-AO8N2kmh$GVe z$Hg*>XVx6FNKE!Mb<Ej{bQZKukmjD&31pgfvXV$A4C8ZiY^*tg@7b~0^c9_}lO`oGH zSoiUarenGEC$}@+e-H1lF;IIGBk_ixCr803`O~(%RpJ|uE|;3$yKtKwLujadXx%*l zhWQg(&mEiYC1jbt`QppU$>EPzwM;iT_~%QM*#4S_8b&kE)bl5#2Mc_SGyj&eSN5~^ z$}J32eU`SoXWWrE5dFOB!_)KS3NO~1Rh};SG&!V2 z!sTG&V(#vUh$y$77pES~xS=4|J@fq*maKwD>Gz$bpSP68)fd-DJv?lGOTt0F@>58X z=-ZU0ztv^41P?~8y_B~+>4`{Ldb{<JBwlFp}E;wbmM5uel?9QaJDz1;M60fA3BNU?5 z!!J(=6aMh_7H>Rzqrd8VU9KB3QTq<=urkrRa$b4otV1v7+5Pz^|2}DoP{_hlQzJ!{ zcVFDQ`c`|s-Nz|A?7w~A?2yA$E%%@8(C*odIZ`j5hMwW^t0@xMV6}7J_AA$C%oPxq zy*Gn#8RwN{Y>Q2HyQgpPNu9P^iA|0x|M0`~x{l|4vP(-PZGLVQnRCVYgGb=m38JU( zGH&3!!G43K^y)gH^<}}+kJy~!x4D!p*<-k(=JwU%e>yhzqH=D3Wi{EBcXX!l-HM+l zygx*nUJ1LoV$E&WjG`xZu5z}rdAw+!(l?vA?jYaQUo4`>9)05a_icTD%P(%f7aX~+ z-{s?7p6_es*qV3v-rx2MKR-Ce)cx0qv;1~Q)-~hsybZO$wtIk@TAsk?DER9V&k|6;eeqqjz7a)+B+;iDgh$|0OPee!q}twXORRYo3hga;b;)#3`Pa4#x5}QUe;4d{sPTm9^e3fRx62oOzhwF6 z<%{(XSAJI(>s0b}u->(=>-77-!i~$s&iMKtV z<-1=s&+0|xrA;gHyDqYK)`dvzxMuwGRrmg?MXwKB-~aUEuUj&H>Xwaq6LXYa`OWN^ z=OrXH;adGuj)hgV8zyE5>t6gKe7yenuj`(FcAh-=@4Dt{w~AsWz1JF9!m7y{M>H09 zdhH6ku-Pj`u;%e8mFJA7U3ugFS+_oYb;$I^sVQgUjZMzRzEu(ri<)`SqUPPx+W`lJ z{!iI*gmuy0sn_<-{?8=5bIL;rMpXvUTbzIEKfV58&&0zJWmdK II;Vst0A0+q{r~^~ literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/github_64.png b/vendor/assets/images/authbuttons/github_64.png new file mode 100644 index 0000000000000000000000000000000000000000..fca7bf44652470a58edd12b410613656da95aed3 GIT binary patch literal 4447 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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 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9FrogmDZn=$tn z0|SF(iEBhjaDG}zd16s2gJVj5QmTSyZen_BP-z7DKNjt&We&yF7b4 zJ0*mUurPNBnksC|TyjN2e&MBQ{cM>F-MRN}E5Goq%KGt}bNBAO`?^k`Zl=Y{+vR27 z=GM-uv`>51@V{EQ{O7wosr||mrcIl6$F*DRYWKS>yZY-~qn|UZdiFf=`|i^+WwzS# z&DY9V=M*bGa&~blkURd!rq9=+DP8&E`s;DLvHqS3@9*vXxpnK-_R!GU4{O)|JGAfd zUWF+V3fyU_iRYKOPkdP{xozUPGL~!W7}AA8w@+kAxi8UP!!|i8`OleyAAT6W=ehHy zJ5kH5=GKiHKX%=Jzy9?L*@NHy&i$~odcw2P!;_yK`>=zpzk5;9xe`l5bN)YW3~w$~ zYUqiE1hxbOO!d-rQTO-o>ACvMe{o91^A9%+CAP4v=xvbdbV++LcX9pq!`cmd*>-qm zOuzX5-Fdr~V~eh_++X$F@$%jUas8@Y$61fAd*qe-Vj107bDiq)NY#!A+rpho!jcy?yjZYH!rOfBf_m$U%rkwS zlT*`)#jb(STSxdy?u%emo`nSe-vFCd)C^~17vSg*w@{}acRGWu2 z50<5xU%Z>Tw|tpx`IR|)=er4ROkTONYg^t+pEo%#J0}}38eMu+DV_QEY(?oB1KIu0 zt1nJox9GU~wI!JvZ}}HnSadxVZwnNW*?zGgtj>!mdv*H5<;H(^mE<7FQ%R}Nj$XV+MFbt!b2xm?{=ya6qsL!QPe|+IZ@rm$V{4+}ueM`7b3SliJedCflUbW`;?AVx zt7lmHZ9nfg;?cQZS@osGrd=Es)wY(5w$ahnY#Va2g-^MvPU*NR?)yEd_G}JwOn$UA zpT)tQvUAiW%O}k+V!6|0>VABxpp30`gYEIEo`NG$zVB>yCZ)yOcK54B@3mD47D~7y zls5Uoij=r0C#n$;&c^E%D8vKL76zldI%cfv&Npl|I5m-)$;j_uUmQ2$abW#=Bw z)&5_YHdiv8vsDh26nkE=F|kzB%X?Fxsk!>ohjG@Mh1g?eZ+!EiT5W>b*0k?Ew%<-l zTG)Je`b=ue+X}w*Hn!~ET<03T=CSM!U@q&DIK&(+J30H4_@lqA`bQ2XUrqSV^xRKh zCC7!2N%Z@9hJL=rxq9sHg~TWPlXX~O8TTxFdt>Yx)0{O0Iu&O7ikwZPcia^|sqeZZvE6OXbXB8!XV_+VIB|TODqTFQ%KuiEA_#-%lS~`f&9M`z^7@?yb_1d#}FB`kT$NSe5EsuQSVc#P-|~NOx7+-vn)9TO&O=rKBXL-wzkNrvQq$E8h$XH_|i&sMMAv!n2*wdbTl8J7Qh ezpZCrW;n?{yLQ*X&Up+B3=E#GelF{r5}E)u2>A8@ 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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 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 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9FrogmDZn=$tn z0|SF(iEBhjaDG}zd16s2gJVj5QmTSyZen_BP-kcwMR!p~;Q zhKksKKeuQ7jPzM*rkUJo8y$TVG+ftQZS@d;zPh5WA!qBE5;n7)x3(a2I5A0p0ES!>cu5F+Cbv0XY?RSxKpDOLP zPYb&ymZe_*@5kMxF$~7X?@L-5)IM2VwA3b8Ph645`NA!!uRnSXi?W26c?=dtUh6T< zx}gxO&eOI2<(+kkw$kxYGi<+KdNY&%eZ}m=WBcT!=GU#h6h3oh*zW}~Iu08>7;boP zZ|=C3{J2*#dqO+wBOap$6~zf0g>z1n`dokW_`!`Yw@voDE9bEbWSRW#Y85!OllhvHLds)@G=9X*hPBD>o<-Wno$$07am!3tx&&>Vx zXzkiH2RBSz_QK=&g%9matnTXW51zhk-ehKYMgQ~k>3NshdzF_y-tlQmQFC5|ftG6Y z$Ayi*BWLeoZ8$h>j-Z8A!^(@cy0=XX@Ax0sQXHImVO3aE@~L@K+;;`!JbBkJ%|dz0 zfq=W4!vixkd|&7=rkZFdEjKcAW#urAUn)@)vVfOiv#7!B(_5}Ry=yyFNoMvf{uwt! z9<%fOGGW=VaSz+`|9iL{Yy{f{HXEK|U$DiC_xH}1PM;;#h&+(^n6!Mc2-}CV`KI%8 zciz5migCI5qt5w)Y(mnD{F+({qc(<_r=4GE%&Hf$y zUOd4hNqgsw5XZ0sGp;>$bLo=wj@_`8<-aYr%lF7A;mcR5!fS5roU}nGFSq+n)ng-J zg=>FRHWj_ytlWP7trthr4GaGti)TISb-VNF(!G6)CQNakRF!{lS?NLPvX#7b4%!*>)ONLWn@1n3YZx$)L{8o#xnn(5x;rZzju;HeiWDrSDiWA z;-}~|->=iaMs1thNE^Mk#n?xU5A+->&5SBsWCaeDRCRO#)`)8;EPJA-5o%xSve==9}fO0~tylqD}8 zxV3U~FT8c{M$=|*{w2G9W?g5=4?ch4cZ}}Et+}?GfkN(Ir?e!@4xDN-ar^1a>Yj_s zHwo)&AC6lxw|B+Hd23HMZtC^$(2BSh6T8R0TH zGOOS1epdFif4kYgU4L%6zc+v7t$k%j$28CDZnX}L4pUyNZEWZF^Xs+$e`@vBg%?j2 z)qfRyd6d)e)R|gKcji#Ol{-|69)@L@Pn27>wR-Dn`?LQGeqFisHak3{cIuoPYV6W4 z!!)W}QdW3%hel~!S|^gB`=HeP^M!3WC)RBGGu799ip!#z?r)xT~)iH^;Y zbr-cW-u+!|+vFV^AG0|96A6lPnjEs$I<*gs;LwK-GTRW;A*H!5apGc~AqW?qQOjoRV&JS~5o z;rEwQ@_xUry0YSyhogO2m1a_OdrOh^a%0O_9o>B<<*9#;umAswJ)MK+DnCQpV`lbw zzn>k{<+uHJ+va@xW}Rl6^A2f3OD6EKFthDF%H(k5=SRh5i%V)J2tJv|xT|ko!9I0` zi_I+uekFdI_raF&^5p0D|6b}8uP=$2fA<|*Q-s?acD|aA+5I^;zu(+%BGvoQO__af z`<{u7j7pmoIDWG!a~EDv;bnOFK*FK#vq0bN+0|i|T60TOY8S4pfByZ!m)?)U{(nE8 z(D!?3u2;gO7=O%a&D}2-4vOlJ+V}8W^tKIojmWIy#qM6@?8x(>}H~QR}I-PB1$2u)v#_F>|k~>%1Tzn?$btAn+ zsj0QP{DSqb2u{|@;t!dNmM{MFj;�!!m)Qh)a96J48I%%fg|{=*M1hRXxK?VRqz; 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 V;mC$h*$fN}44$rjF6*2UngHt=Pv-yt literal 0 HcmV?d00001 From c5ae1549a1d09599734ccaab27e90a8752f1ede6 Mon Sep 17 00:00:00 2001 From: Florian Unglaub 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