Commit 496ec8b26d2147f1b14c41cc17d2224a7d8c0898
Exists in
master
and in
4 other branches
Merge branch 'refactor/oauth' of /home/git/repositories/gitlab/gitlabhq
Showing
13 changed files
with
372 additions
and
322 deletions
Show diff stats
app/controllers/omniauth_callbacks_controller.rb
@@ -16,35 +16,41 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController | @@ -16,35 +16,41 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController | ||
16 | end | 16 | end |
17 | 17 | ||
18 | def ldap | 18 | def ldap |
19 | - # We only find ourselves here if the authentication to LDAP was successful. | ||
20 | - @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) | ||
21 | - if @user.persisted? | ||
22 | - @user.remember_me = true | ||
23 | - end | ||
24 | - sign_in_and_redirect @user | 19 | + # We only find ourselves here |
20 | + # if the authentication to LDAP was successful. | ||
21 | + @user = Gitlab::LDAP::User.find_or_create(oauth) | ||
22 | + @user.remember_me = true if @user.persisted? | ||
23 | + sign_in_and_redirect(@user) | ||
25 | end | 24 | end |
26 | 25 | ||
27 | private | 26 | private |
28 | 27 | ||
29 | def handle_omniauth | 28 | def handle_omniauth |
30 | - oauth = request.env['omniauth.auth'] | ||
31 | - provider, uid = oauth['provider'], oauth['uid'] | ||
32 | - | ||
33 | if current_user | 29 | if current_user |
34 | # Change a logged-in user's authentication method: | 30 | # Change a logged-in user's authentication method: |
35 | - current_user.extern_uid = uid | ||
36 | - current_user.provider = provider | 31 | + current_user.extern_uid = oauth['uid'] |
32 | + current_user.provider = oauth['provider'] | ||
37 | current_user.save | 33 | current_user.save |
38 | redirect_to profile_path | 34 | redirect_to profile_path |
39 | else | 35 | else |
40 | - @user = User.find_or_new_for_omniauth(oauth) | 36 | + @user = Gitlab::OAuth::User.find(oauth) |
37 | + | ||
38 | + # Create user if does not exist | ||
39 | + # and allow_single_sign_on is true | ||
40 | + if Gitlab.config.omniauth['allow_single_sign_on'] | ||
41 | + @user ||= Gitlab::OAuth::User.create(oauth) | ||
42 | + end | ||
41 | 43 | ||
42 | if @user | 44 | if @user |
43 | - sign_in_and_redirect @user | 45 | + sign_in_and_redirect(@user) |
44 | else | 46 | else |
45 | flash[:notice] = "There's no such user!" | 47 | flash[:notice] = "There's no such user!" |
46 | redirect_to new_user_session_path | 48 | redirect_to new_user_session_path |
47 | end | 49 | end |
48 | end | 50 | end |
49 | end | 51 | end |
52 | + | ||
53 | + def oauth | ||
54 | + @oauth ||= request.env['omniauth.auth'] | ||
55 | + end | ||
50 | end | 56 | end |
app/controllers/registrations_controller.rb
@@ -2,9 +2,6 @@ class RegistrationsController < Devise::RegistrationsController | @@ -2,9 +2,6 @@ class RegistrationsController < Devise::RegistrationsController | ||
2 | before_filter :signup_enabled? | 2 | before_filter :signup_enabled? |
3 | 3 | ||
4 | def destroy | 4 | def destroy |
5 | - if current_user.owned_projects.count > 0 | ||
6 | - redirect_to account_profile_path, alert: "Remove projects and groups before removing account." and return | ||
7 | - end | ||
8 | current_user.destroy | 5 | current_user.destroy |
9 | 6 | ||
10 | respond_to do |format| | 7 | respond_to do |format| |
app/helpers/profile_helper.rb
@@ -4,4 +4,16 @@ module ProfileHelper | @@ -4,4 +4,16 @@ module ProfileHelper | ||
4 | 'active' | 4 | 'active' |
5 | end | 5 | end |
6 | end | 6 | end |
7 | + | ||
8 | + def show_profile_username_tab? | ||
9 | + current_user.can_change_username? | ||
10 | + end | ||
11 | + | ||
12 | + def show_profile_social_tab? | ||
13 | + Gitlab.config.omniauth.enabled && !current_user.ldap_user? | ||
14 | + end | ||
15 | + | ||
16 | + def show_profile_remove_tab? | ||
17 | + Gitlab.config.gitlab.signup_enabled && !current_user.ldap_user? | ||
18 | + end | ||
7 | end | 19 | end |
app/models/user.rb
@@ -159,6 +159,7 @@ class User < ActiveRecord::Base | @@ -159,6 +159,7 @@ class User < ActiveRecord::Base | ||
159 | scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } | 159 | scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } |
160 | scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : scoped } | 160 | scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : scoped } |
161 | scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') } | 161 | scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') } |
162 | + scope :ldap, -> { where(provider: 'ldap') } | ||
162 | 163 | ||
163 | scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } | 164 | scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } |
164 | 165 | ||
@@ -186,22 +187,6 @@ class User < ActiveRecord::Base | @@ -186,22 +187,6 @@ class User < ActiveRecord::Base | ||
186 | end | 187 | end |
187 | end | 188 | end |
188 | 189 | ||
189 | - def create_from_omniauth(auth, ldap = false) | ||
190 | - gitlab_auth.create_from_omniauth(auth, ldap) | ||
191 | - end | ||
192 | - | ||
193 | - def find_or_new_for_omniauth(auth) | ||
194 | - gitlab_auth.find_or_new_for_omniauth(auth) | ||
195 | - end | ||
196 | - | ||
197 | - def find_for_ldap_auth(auth, signed_in_resource = nil) | ||
198 | - gitlab_auth.find_for_ldap_auth(auth, signed_in_resource) | ||
199 | - end | ||
200 | - | ||
201 | - def gitlab_auth | ||
202 | - Gitlab::Auth.new | ||
203 | - end | ||
204 | - | ||
205 | def search query | 190 | def search query |
206 | where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") | 191 | where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") |
207 | end | 192 | end |
app/views/profiles/account.html.haml
@@ -5,91 +5,128 @@ | @@ -5,91 +5,128 @@ | ||
5 | - if current_user.ldap_user? | 5 | - if current_user.ldap_user? |
6 | Some options are unavailable for LDAP accounts | 6 | Some options are unavailable for LDAP accounts |
7 | %hr | 7 | %hr |
8 | -- unless current_user.ldap_user? | ||
9 | - - if Gitlab.config.omniauth.enabled | ||
10 | - %fieldset | ||
11 | - %legend Social Accounts | ||
12 | - .oauth_select_holder | ||
13 | - %p.hint Tip: Click on icon to activate signin with one of the following services | ||
14 | - - enabled_social_providers.each do |provider| | ||
15 | - %span{class: oauth_active_class(provider) } | ||
16 | - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) | ||
17 | 8 | ||
18 | 9 | ||
19 | - %fieldset.update-password | ||
20 | - %legend Password | ||
21 | - = form_for @user, url: update_password_profile_path, method: :put do |f| | ||
22 | - .padded | ||
23 | - %p.slead After a successful password update you will be redirected to login page where you should login with your new password | ||
24 | - -if @user.errors.any? | ||
25 | - .alert.alert-error | ||
26 | - %ul | ||
27 | - - @user.errors.full_messages.each do |msg| | ||
28 | - %li= msg | 10 | +.row |
11 | + .span2 | ||
12 | + %ul.nav.nav-pills.nav-stacked.nav-stacked-menu | ||
13 | + %li.active | ||
14 | + = link_to '#tab-token', 'data-toggle' => 'tab' do | ||
15 | + Private Token | ||
16 | + %li | ||
17 | + = link_to '#tab-password', 'data-toggle' => 'tab' do | ||
18 | + Password | ||
29 | 19 | ||
30 | - .control-group | ||
31 | - = f.label :password | ||
32 | - .controls= f.password_field :password, required: true | ||
33 | - .control-group | ||
34 | - = f.label :password_confirmation | ||
35 | - .controls | ||
36 | - = f.password_field :password_confirmation, required: true | ||
37 | - .control-group | ||
38 | - .controls | ||
39 | - = f.submit 'Save password', class: "btn btn-save" | 20 | + - if show_profile_social_tab? |
21 | + %li | ||
22 | + = link_to '#tab-social', 'data-toggle' => 'tab' do | ||
23 | + Social Accounts | ||
40 | 24 | ||
25 | + - if show_profile_username_tab? | ||
26 | + %li | ||
27 | + = link_to '#tab-username', 'data-toggle' => 'tab' do | ||
28 | + Change Username | ||
41 | 29 | ||
30 | + - if show_profile_remove_tab? | ||
31 | + %li | ||
32 | + = link_to '#tab-remove', 'data-toggle' => 'tab' do | ||
33 | + Remove Account | ||
34 | + .span10 | ||
35 | + .tab-content | ||
36 | + .tab-pane.active#tab-token | ||
37 | + %fieldset.update-token | ||
38 | + %legend | ||
39 | + Private token | ||
40 | + %span.cred.pull-right | ||
41 | + keep it secret! | ||
42 | + %div | ||
43 | + = form_for @user, url: reset_private_token_profile_path, method: :put do |f| | ||
44 | + .data | ||
45 | + %p.slead | ||
46 | + Your private token is used to access application resources without authentication. | ||
47 | + %br | ||
48 | + It can be used for atom feeds or the API. | ||
49 | + %p.cgray | ||
50 | + - if current_user.private_token | ||
51 | + = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding" | ||
52 | + = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" | ||
53 | + - else | ||
54 | + %span You don`t have one yet. Click generate to fix it. | ||
55 | + = f.submit 'Generate', class: "btn success btn-build-token" | ||
42 | 56 | ||
43 | -%fieldset.update-token | ||
44 | - %legend | ||
45 | - Private token | ||
46 | - %span.cred.pull-right | ||
47 | - keep it secret! | ||
48 | - .padded | ||
49 | - = form_for @user, url: reset_private_token_profile_path, method: :put do |f| | ||
50 | - .data | ||
51 | - %p.slead | ||
52 | - Your private token is used to access application resources without authentication. | ||
53 | - %br | ||
54 | - It can be used for atom feeds or the API. | ||
55 | - %p.cgray | ||
56 | - - if current_user.private_token | ||
57 | - = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding" | ||
58 | - = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" | ||
59 | - - else | ||
60 | - %span You don`t have one yet. Click generate to fix it. | ||
61 | - = f.submit 'Generate', class: "btn success btn-build-token" | 57 | + .tab-pane#tab-password |
58 | + %fieldset.update-password | ||
59 | + %legend Password | ||
60 | + = form_for @user, url: update_password_profile_path, method: :put do |f| | ||
61 | + %div | ||
62 | + %p.slead After a successful password update you will be redirected to login page where you should login with your new password | ||
63 | + -if @user.errors.any? | ||
64 | + .alert.alert-error | ||
65 | + %ul | ||
66 | + - @user.errors.full_messages.each do |msg| | ||
67 | + %li= msg | ||
68 | + .control-group | ||
69 | + = f.label :password | ||
70 | + .controls= f.password_field :password, required: true | ||
71 | + .control-group | ||
72 | + = f.label :password_confirmation | ||
73 | + .controls | ||
74 | + = f.password_field :password_confirmation, required: true | ||
75 | + .control-group | ||
76 | + .controls | ||
77 | + = f.submit 'Save password', class: "btn btn-save" | ||
62 | 78 | ||
79 | + - if show_profile_social_tab? | ||
80 | + .tab-pane#tab-social | ||
81 | + %fieldset | ||
82 | + %legend Social Accounts | ||
83 | + .oauth_select_holder | ||
84 | + %p.hint Tip: Click on icon to activate signin with one of the following services | ||
85 | + - enabled_social_providers.each do |provider| | ||
86 | + %span{class: oauth_active_class(provider) } | ||
87 | + = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) | ||
63 | 88 | ||
64 | -- if current_user.can_change_username? | ||
65 | - %fieldset.update-username | ||
66 | - %legend | ||
67 | - Username | ||
68 | - %small.cred.pull-right | ||
69 | - Changing your username can have unintended side effects! | ||
70 | - = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| | ||
71 | - .padded | ||
72 | - = f.label :username | ||
73 | - .controls | ||
74 | - = f.text_field :username, required: true | ||
75 | - | ||
76 | - %span.loading-gif.hide= image_tag "ajax_loader.gif" | ||
77 | - %span.update-success.cgreen.hide | ||
78 | - %i.icon-ok | ||
79 | - Saved | ||
80 | - %span.update-failed.cred.hide | ||
81 | - %i.icon-remove | ||
82 | - Failed | ||
83 | - %ul.cred | ||
84 | - %li This will change the web URL for personal projects. | ||
85 | - %li This will change the git path to repositories for personal projects. | ||
86 | - .controls | ||
87 | - = f.submit 'Save username', class: "btn btn-save" | 89 | + - if show_profile_username_tab? |
90 | + .tab-pane#tab-username | ||
91 | + %fieldset.update-username | ||
92 | + %legend | ||
93 | + Username | ||
94 | + %small.cred.pull-right | ||
95 | + Changing your username can have unintended side effects! | ||
96 | + = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| | ||
97 | + %div | ||
98 | + .control-group | ||
99 | + = f.label :username | ||
100 | + .controls | ||
101 | + = f.text_field :username, required: true | ||
102 | + | ||
103 | + %span.loading-gif.hide= image_tag "ajax_loader.gif" | ||
104 | + %span.update-success.cgreen.hide | ||
105 | + %i.icon-ok | ||
106 | + Saved | ||
107 | + %span.update-failed.cred.hide | ||
108 | + %i.icon-remove | ||
109 | + Failed | ||
110 | + %ul.cred | ||
111 | + %li This will change the web URL for personal projects. | ||
112 | + %li This will change the git path to repositories for personal projects. | ||
113 | + .controls | ||
114 | + = f.submit 'Save username', class: "btn btn-save" | ||
88 | 115 | ||
89 | -- if gitlab_config.signup_enabled | ||
90 | - %fieldset.remove-account | ||
91 | - %legend | ||
92 | - Remove account | ||
93 | - %small.cred.pull-right | ||
94 | - Before removing the account you must remove all projects! | ||
95 | - = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right" | 116 | + - if show_profile_remove_tab? |
117 | + .tab-pane#tab-remove | ||
118 | + %fieldset.remove-account | ||
119 | + %legend | ||
120 | + Remove account | ||
121 | + %div | ||
122 | + %p Deleting an account has the following effects: | ||
123 | + %ul | ||
124 | + %li All user content like authored issues, snippets, comments will be removed | ||
125 | + - rp = current_user.personal_projects.count | ||
126 | + - unless rp.zero? | ||
127 | + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored | ||
128 | + - if current_user.solo_owned_groups.present? | ||
129 | + %li | ||
130 | + Next groups will be abandoned. You should transfer or remove them: | ||
131 | + %strong #{current_user.solo_owned_groups.map(&:name).join(', ')} | ||
132 | + = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove" |
lib/gitlab/auth.rb
@@ -7,97 +7,16 @@ module Gitlab | @@ -7,97 +7,16 @@ module Gitlab | ||
7 | # Second chance - try LDAP authentication | 7 | # Second chance - try LDAP authentication |
8 | return nil unless ldap_conf.enabled | 8 | return nil unless ldap_conf.enabled |
9 | 9 | ||
10 | - ldap_auth(login, password) | 10 | + Gitlab::LDAP::User.authenticate(login, password) |
11 | else | 11 | else |
12 | user if user.valid_password?(password) | 12 | user if user.valid_password?(password) |
13 | end | 13 | end |
14 | end | 14 | end |
15 | 15 | ||
16 | - def find_for_ldap_auth(auth, signed_in_resource = nil) | ||
17 | - uid = auth.info.uid | ||
18 | - provider = auth.provider | ||
19 | - email = auth.info.email.downcase unless auth.info.email.nil? | ||
20 | - raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil? | ||
21 | - | ||
22 | - if @user = User.find_by_extern_uid_and_provider(uid, provider) | ||
23 | - @user | ||
24 | - elsif @user = User.find_by_email(email) | ||
25 | - log.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}" | ||
26 | - @user.update_attributes(extern_uid: uid, provider: provider) | ||
27 | - @user | ||
28 | - else | ||
29 | - create_from_omniauth(auth, true) | ||
30 | - end | ||
31 | - end | ||
32 | - | ||
33 | - def create_from_omniauth(auth, ldap = false) | ||
34 | - provider = auth.provider | ||
35 | - uid = auth.info.uid || auth.uid | ||
36 | - uid = uid.to_s.force_encoding("utf-8") | ||
37 | - name = auth.info.name.to_s.force_encoding("utf-8") | ||
38 | - email = auth.info.email.to_s.downcase unless auth.info.email.nil? | ||
39 | - | ||
40 | - ldap_prefix = ldap ? '(LDAP) ' : '' | ||
41 | - raise OmniAuth::Error, "#{ldap_prefix}#{provider} does not provide an email"\ | ||
42 | - " address" if auth.info.email.blank? | ||
43 | - | ||
44 | - log.info "#{ldap_prefix}Creating user from #{provider} login"\ | ||
45 | - " {uid => #{uid}, name => #{name}, email => #{email}}" | ||
46 | - password = Devise.friendly_token[0, 8].downcase | ||
47 | - @user = User.new({ | ||
48 | - extern_uid: uid, | ||
49 | - provider: provider, | ||
50 | - name: name, | ||
51 | - username: email.match(/^[^@]*/)[0], | ||
52 | - email: email, | ||
53 | - password: password, | ||
54 | - password_confirmation: password, | ||
55 | - }, as: :admin).with_defaults | ||
56 | - @user.save! | ||
57 | - | ||
58 | - if Gitlab.config.omniauth['block_auto_created_users'] && !ldap | ||
59 | - @user.block | ||
60 | - end | ||
61 | - | ||
62 | - @user | ||
63 | - end | ||
64 | - | ||
65 | - def find_or_new_for_omniauth(auth) | ||
66 | - provider, uid = auth.provider, auth.uid | ||
67 | - email = auth.info.email.downcase unless auth.info.email.nil? | ||
68 | - | ||
69 | - if @user = User.find_by_provider_and_extern_uid(provider, uid) | ||
70 | - @user | ||
71 | - elsif @user = User.find_by_email(email) | ||
72 | - @user.update_attributes(extern_uid: uid, provider: provider) | ||
73 | - @user | ||
74 | - else | ||
75 | - if Gitlab.config.omniauth['allow_single_sign_on'] | ||
76 | - @user = create_from_omniauth(auth) | ||
77 | - @user | ||
78 | - end | ||
79 | - end | ||
80 | - end | ||
81 | - | ||
82 | def log | 16 | def log |
83 | Gitlab::AppLogger | 17 | Gitlab::AppLogger |
84 | end | 18 | end |
85 | 19 | ||
86 | - def ldap_auth(login, password) | ||
87 | - # Check user against LDAP backend if user is not authenticated | ||
88 | - # Only check with valid login and password to prevent anonymous bind results | ||
89 | - return nil unless ldap_conf.enabled && !login.blank? && !password.blank? | ||
90 | - | ||
91 | - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) | ||
92 | - ldap_user = ldap.bind_as( | ||
93 | - filter: Net::LDAP::Filter.eq(ldap.uid, login), | ||
94 | - size: 1, | ||
95 | - password: password | ||
96 | - ) | ||
97 | - | ||
98 | - User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user | ||
99 | - end | ||
100 | - | ||
101 | def ldap_conf | 20 | def ldap_conf |
102 | @ldap_conf ||= Gitlab.config.ldap | 21 | @ldap_conf ||= Gitlab.config.ldap |
103 | end | 22 | end |
lib/gitlab/backend/grack_auth.rb
1 | require_relative 'shell_env' | 1 | require_relative 'shell_env' |
2 | -require_relative 'grack_ldap' | ||
3 | require_relative 'grack_helpers' | 2 | require_relative 'grack_helpers' |
4 | 3 | ||
5 | module Grack | 4 | module Grack |
6 | class Auth < Rack::Auth::Basic | 5 | class Auth < Rack::Auth::Basic |
7 | - include LDAP | ||
8 | include Helpers | 6 | include Helpers |
9 | 7 | ||
10 | attr_accessor :user, :project, :ref, :env | 8 | attr_accessor :user, :project, :ref, :env |
lib/gitlab/backend/grack_ldap.rb
@@ -1,24 +0,0 @@ | @@ -1,24 +0,0 @@ | ||
1 | -require 'omniauth-ldap' | ||
2 | - | ||
3 | -module Grack | ||
4 | - module LDAP | ||
5 | - def ldap_auth(login, password) | ||
6 | - # Check user against LDAP backend if user is not authenticated | ||
7 | - # Only check with valid login and password to prevent anonymous bind results | ||
8 | - return nil unless ldap_conf.enabled && !login.blank? && !password.blank? | ||
9 | - | ||
10 | - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) | ||
11 | - ldap_user = ldap.bind_as( | ||
12 | - filter: Net::LDAP::Filter.eq(ldap.uid, login), | ||
13 | - size: 1, | ||
14 | - password: password | ||
15 | - ) | ||
16 | - | ||
17 | - User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user | ||
18 | - end | ||
19 | - | ||
20 | - def ldap_conf | ||
21 | - @ldap_conf ||= Gitlab.config.ldap | ||
22 | - end | ||
23 | - end | ||
24 | -end |
@@ -0,0 +1,81 @@ | @@ -0,0 +1,81 @@ | ||
1 | +require 'gitlab/oauth/user' | ||
2 | + | ||
3 | +# LDAP extension for User model | ||
4 | +# | ||
5 | +# * Find or create user from omniauth.auth data | ||
6 | +# * Links LDAP account with existing user | ||
7 | +# * Auth LDAP user with login and password | ||
8 | +# | ||
9 | +module Gitlab | ||
10 | + module LDAP | ||
11 | + class User < Gitlab::OAuth::User | ||
12 | + class << self | ||
13 | + def find_or_create(auth) | ||
14 | + @auth = auth | ||
15 | + | ||
16 | + if uid.blank? || email.blank? | ||
17 | + raise_error("Account must provide an uid and email address") | ||
18 | + end | ||
19 | + | ||
20 | + user = find(auth) | ||
21 | + | ||
22 | + unless user | ||
23 | + # Look for user with same emails | ||
24 | + # | ||
25 | + # Possible cases: | ||
26 | + # * When user already has account and need to link his LDAP account. | ||
27 | + # * LDAP uid changed for user with same email and we need to update his uid | ||
28 | + # | ||
29 | + user = model.find_by_email(email) | ||
30 | + | ||
31 | + if user | ||
32 | + user.update_attributes(extern_uid: uid, provider: provider) | ||
33 | + log.info("(LDAP) Updating legacy LDAP user #{email} with extern_uid => #{uid}") | ||
34 | + else | ||
35 | + # Create a new user inside GitLab database | ||
36 | + # based on LDAP credentials | ||
37 | + # | ||
38 | + # | ||
39 | + user = create(auth) | ||
40 | + end | ||
41 | + end | ||
42 | + | ||
43 | + user | ||
44 | + end | ||
45 | + | ||
46 | + def authenticate(login, password) | ||
47 | + # Check user against LDAP backend if user is not authenticated | ||
48 | + # Only check with valid login and password to prevent anonymous bind results | ||
49 | + return nil unless ldap_conf.enabled && login.present? && password.present? | ||
50 | + | ||
51 | + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) | ||
52 | + ldap_user = ldap.bind_as( | ||
53 | + filter: Net::LDAP::Filter.eq(ldap.uid, login), | ||
54 | + size: 1, | ||
55 | + password: password | ||
56 | + ) | ||
57 | + | ||
58 | + find_by_uid(ldap_user.dn) if ldap_user | ||
59 | + end | ||
60 | + | ||
61 | + private | ||
62 | + | ||
63 | + def find_by_uid(uid) | ||
64 | + model.where(provider: provider, extern_uid: uid).last | ||
65 | + end | ||
66 | + | ||
67 | + def provider | ||
68 | + 'ldap' | ||
69 | + end | ||
70 | + | ||
71 | + def raise_error(message) | ||
72 | + raise OmniAuth::Error, "(LDAP) " + message | ||
73 | + end | ||
74 | + | ||
75 | + def ldap_conf | ||
76 | + Gitlab.config.ldap | ||
77 | + end | ||
78 | + end | ||
79 | + end | ||
80 | + end | ||
81 | +end |
@@ -0,0 +1,85 @@ | @@ -0,0 +1,85 @@ | ||
1 | +# OAuth extension for User model | ||
2 | +# | ||
3 | +# * Find GitLab user based on omniauth uid and provider | ||
4 | +# * Create new user from omniauth data | ||
5 | +# | ||
6 | +module Gitlab | ||
7 | + module OAuth | ||
8 | + class User | ||
9 | + class << self | ||
10 | + attr_reader :auth | ||
11 | + | ||
12 | + def find(auth) | ||
13 | + @auth = auth | ||
14 | + find_by_uid_and_provider | ||
15 | + end | ||
16 | + | ||
17 | + def create(auth) | ||
18 | + @auth = auth | ||
19 | + password = Devise.friendly_token[0, 8].downcase | ||
20 | + opts = { | ||
21 | + extern_uid: uid, | ||
22 | + provider: provider, | ||
23 | + name: name, | ||
24 | + username: username, | ||
25 | + email: email, | ||
26 | + password: password, | ||
27 | + password_confirmation: password, | ||
28 | + } | ||
29 | + | ||
30 | + user = model.new(opts, as: :admin).with_defaults | ||
31 | + user.save! | ||
32 | + log.info "(OAuth) Creating user #{email} from login with extern_uid => #{uid}" | ||
33 | + | ||
34 | + if Gitlab.config.omniauth['block_auto_created_users'] && !ldap? | ||
35 | + user.block | ||
36 | + end | ||
37 | + | ||
38 | + user | ||
39 | + end | ||
40 | + | ||
41 | + private | ||
42 | + | ||
43 | + def find_by_uid_and_provider | ||
44 | + model.where(provider: provider, extern_uid: uid).last | ||
45 | + end | ||
46 | + | ||
47 | + def uid | ||
48 | + auth.info.uid || auth.uid | ||
49 | + end | ||
50 | + | ||
51 | + def email | ||
52 | + auth.info.email.downcase unless auth.info.email.nil? | ||
53 | + end | ||
54 | + | ||
55 | + def name | ||
56 | + auth.info.name.to_s.force_encoding("utf-8") | ||
57 | + end | ||
58 | + | ||
59 | + def username | ||
60 | + email.match(/^[^@]*/)[0] | ||
61 | + end | ||
62 | + | ||
63 | + def provider | ||
64 | + auth.provider | ||
65 | + end | ||
66 | + | ||
67 | + def log | ||
68 | + Gitlab::AppLogger | ||
69 | + end | ||
70 | + | ||
71 | + def model | ||
72 | + ::User | ||
73 | + end | ||
74 | + | ||
75 | + def raise_error(message) | ||
76 | + raise OmniAuth::Error, "(OAuth) " + message | ||
77 | + end | ||
78 | + | ||
79 | + def ldap? | ||
80 | + provider == 'ldap' | ||
81 | + end | ||
82 | + end | ||
83 | + end | ||
84 | + end | ||
85 | +end |
spec/features/profile_spec.rb
@@ -17,26 +17,12 @@ describe "Profile account page" do | @@ -17,26 +17,12 @@ describe "Profile account page" do | ||
17 | 17 | ||
18 | it { page.should have_content("Remove account") } | 18 | it { page.should have_content("Remove account") } |
19 | 19 | ||
20 | - it "should delete the account", js: true do | 20 | + it "should delete the account" do |
21 | expect { click_link "Delete account" }.to change {User.count}.by(-1) | 21 | expect { click_link "Delete account" }.to change {User.count}.by(-1) |
22 | current_path.should == new_user_session_path | 22 | current_path.should == new_user_session_path |
23 | end | 23 | end |
24 | end | 24 | end |
25 | 25 | ||
26 | - describe "when signup is enabled and user has a project" do | ||
27 | - before do | ||
28 | - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) | ||
29 | - @project = create(:project, namespace: @user.namespace) | ||
30 | - @project.team << [@user, :master] | ||
31 | - visit account_profile_path | ||
32 | - end | ||
33 | - it { page.should have_content("Remove account") } | ||
34 | - | ||
35 | - it "should not allow user to delete the account" do | ||
36 | - expect { click_link "Delete account" }.not_to change {User.count}.by(-1) | ||
37 | - end | ||
38 | - end | ||
39 | - | ||
40 | describe "when signup is disabled" do | 26 | describe "when signup is disabled" do |
41 | before do | 27 | before do |
42 | Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) | 28 | Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) |
spec/lib/auth_spec.rb
@@ -3,102 +3,26 @@ require 'spec_helper' | @@ -3,102 +3,26 @@ require 'spec_helper' | ||
3 | describe Gitlab::Auth do | 3 | describe Gitlab::Auth do |
4 | let(:gl_auth) { Gitlab::Auth.new } | 4 | let(:gl_auth) { Gitlab::Auth.new } |
5 | 5 | ||
6 | - before do | ||
7 | - Gitlab.config.stub(omniauth: {}) | ||
8 | - | ||
9 | - @info = mock( | ||
10 | - uid: '12djsak321', | ||
11 | - name: 'John', | ||
12 | - email: 'john@mail.com' | ||
13 | - ) | ||
14 | - end | ||
15 | - | ||
16 | - describe :find_for_ldap_auth do | 6 | + describe :find do |
17 | before do | 7 | before do |
18 | - @auth = mock( | ||
19 | - uid: '12djsak321', | ||
20 | - info: @info, | ||
21 | - provider: 'ldap' | 8 | + @user = create( |
9 | + :user, | ||
10 | + username: 'john', | ||
11 | + password: '888777', | ||
12 | + password_confirmation: '888777' | ||
22 | ) | 13 | ) |
23 | end | 14 | end |
24 | 15 | ||
25 | - it "should find by uid & provider" do | ||
26 | - User.should_receive :find_by_extern_uid_and_provider | ||
27 | - gl_auth.find_for_ldap_auth(@auth) | ||
28 | - end | ||
29 | - | ||
30 | - it "should update credentials by email if missing uid" do | ||
31 | - user = double('User') | ||
32 | - User.stub find_by_extern_uid_and_provider: nil | ||
33 | - User.stub find_by_email: user | ||
34 | - user.should_receive :update_attributes | ||
35 | - gl_auth.find_for_ldap_auth(@auth) | ||
36 | - end | ||
37 | - | ||
38 | - | ||
39 | - it "should create from auth if user does not exist"do | ||
40 | - User.stub find_by_extern_uid_and_provider: nil | ||
41 | - User.stub find_by_email: nil | ||
42 | - gl_auth.should_receive :create_from_omniauth | ||
43 | - gl_auth.find_for_ldap_auth(@auth) | ||
44 | - end | ||
45 | - end | ||
46 | - | ||
47 | - describe :find_or_new_for_omniauth do | ||
48 | - before do | ||
49 | - @auth = mock( | ||
50 | - info: @info, | ||
51 | - provider: 'twitter', | ||
52 | - uid: '12djsak321', | ||
53 | - ) | 16 | + it "should find user by valid login/password" do |
17 | + gl_auth.find('john', '888777').should == @user | ||
54 | end | 18 | end |
55 | 19 | ||
56 | - it "should find user"do | ||
57 | - User.should_receive :find_by_provider_and_extern_uid | ||
58 | - gl_auth.should_not_receive :create_from_omniauth | ||
59 | - gl_auth.find_or_new_for_omniauth(@auth) | 20 | + it "should not find user with invalid password" do |
21 | + gl_auth.find('john', 'invalid').should_not == @user | ||
60 | end | 22 | end |
61 | 23 | ||
62 | - it "should not create user"do | ||
63 | - User.stub find_by_provider_and_extern_uid: nil | ||
64 | - gl_auth.should_not_receive :create_from_omniauth | ||
65 | - gl_auth.find_or_new_for_omniauth(@auth) | ||
66 | - end | ||
67 | - | ||
68 | - it "should create user if single_sing_on"do | ||
69 | - Gitlab.config.omniauth['allow_single_sign_on'] = true | ||
70 | - User.stub find_by_provider_and_extern_uid: nil | ||
71 | - gl_auth.should_receive :create_from_omniauth | ||
72 | - gl_auth.find_or_new_for_omniauth(@auth) | ||
73 | - end | ||
74 | - end | ||
75 | - | ||
76 | - describe :create_from_omniauth do | ||
77 | - it "should create user from LDAP" do | ||
78 | - @auth = mock(info: @info, provider: 'ldap') | ||
79 | - user = gl_auth.create_from_omniauth(@auth, true) | ||
80 | - | ||
81 | - user.should be_valid | ||
82 | - user.extern_uid.should == @info.uid | ||
83 | - user.provider.should == 'ldap' | ||
84 | - end | ||
85 | - | ||
86 | - it "should create user from Omniauth" do | ||
87 | - @auth = mock(info: @info, provider: 'twitter') | ||
88 | - user = gl_auth.create_from_omniauth(@auth, false) | ||
89 | - | ||
90 | - user.should be_valid | ||
91 | - user.extern_uid.should == @info.uid | ||
92 | - user.provider.should == 'twitter' | ||
93 | - end | ||
94 | - | ||
95 | - it "should apply defaults to user" do | ||
96 | - @auth = mock(info: @info, provider: 'ldap') | ||
97 | - user = gl_auth.create_from_omniauth(@auth, true) | ||
98 | - | ||
99 | - user.should be_valid | ||
100 | - user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit | ||
101 | - user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group | 24 | + it "should not find user with invalid login and password" do |
25 | + gl_auth.find('jon', 'invalid').should_not == @user | ||
102 | end | 26 | end |
103 | end | 27 | end |
104 | end | 28 | end |
@@ -0,0 +1,44 @@ | @@ -0,0 +1,44 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Gitlab::OAuth::User do | ||
4 | + let(:gl_auth) { Gitlab::OAuth::User } | ||
5 | + | ||
6 | + before do | ||
7 | + Gitlab.config.stub(omniauth: {}) | ||
8 | + | ||
9 | + @info = mock( | ||
10 | + uid: '12djsak321', | ||
11 | + name: 'John', | ||
12 | + email: 'john@mail.com' | ||
13 | + ) | ||
14 | + end | ||
15 | + | ||
16 | + describe :create do | ||
17 | + it "should create user from LDAP" do | ||
18 | + @auth = mock(info: @info, provider: 'ldap') | ||
19 | + user = gl_auth.create(@auth) | ||
20 | + | ||
21 | + user.should be_valid | ||
22 | + user.extern_uid.should == @info.uid | ||
23 | + user.provider.should == 'ldap' | ||
24 | + end | ||
25 | + | ||
26 | + it "should create user from Omniauth" do | ||
27 | + @auth = mock(info: @info, provider: 'twitter') | ||
28 | + user = gl_auth.create(@auth) | ||
29 | + | ||
30 | + user.should be_valid | ||
31 | + user.extern_uid.should == @info.uid | ||
32 | + user.provider.should == 'twitter' | ||
33 | + end | ||
34 | + | ||
35 | + it "should apply defaults to user" do | ||
36 | + @auth = mock(info: @info, provider: 'ldap') | ||
37 | + user = gl_auth.create(@auth) | ||
38 | + | ||
39 | + user.should be_valid | ||
40 | + user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit | ||
41 | + user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group | ||
42 | + end | ||
43 | + end | ||
44 | +end |