diff --git a/Gemfile b/Gemfile index 0a5f730d..8e569c5b 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,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/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 80446a4c..201c69f4 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -134,7 +134,7 @@ $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 d472936b..3be285ba 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. @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) @@ -19,4 +19,33 @@ 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.extern_uid = uid + current_user.provider = provider + current_user.save + redirect_to profile_path + else + @user = User.find_or_new_for_omniauth(oauth) + + 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 3dafb753..7033daf8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -135,4 +135,9 @@ module ApplicationHelper "Never" end 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/models/user.rb b/app/models/user.rb index ad6af6a6..fa5d6834 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,10 +86,50 @@ class User < ActiveRecord::Base where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') 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 + 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 + end + 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? @@ -101,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 @@ -148,4 +178,3 @@ end # bio :string(255) # blocked :boolean(1) default(FALSE), not null # - diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb new file mode 100644 index 00000000..6b334b87 --- /dev/null +++ b/app/views/devise/sessions/new.html.erb @@ -0,0 +1,32 @@ +<% 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? %> +
+
+ +
+ <% end -%> + + <% end %> + +<% else %> + <%= render :partial => 'devise/sessions/new_ldap' %> +<% 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 d0aee7ac..2405b9d7 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 save-btn" diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml index 22e840a0..1e53ead6 100644 --- a/app/views/profile/show.html.haml +++ b/app/views/profile/show.html.haml @@ -50,6 +50,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 08e3427f..809d7ee9 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -50,3 +50,21 @@ 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 df9ccf32..00b7cc09 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 @@ -120,6 +120,14 @@ class Settings < Settingslogic app['backup_keep_time'] || 0 end + def omniauth_enabled? + omniauth['enabled'] || false + end + + def omniauth_providers + omniauth['providers'] || [] + end + def disable_gravatar? app['disable_gravatar'] || false end diff --git a/vendor/assets/images/authbuttons/github_32.png b/vendor/assets/images/authbuttons/github_32.png new file mode 100644 index 00000000..247e52a5 Binary files /dev/null and b/vendor/assets/images/authbuttons/github_32.png differ diff --git a/vendor/assets/images/authbuttons/github_64.png b/vendor/assets/images/authbuttons/github_64.png new file mode 100644 index 00000000..fca7bf44 Binary files /dev/null and b/vendor/assets/images/authbuttons/github_64.png differ diff --git a/vendor/assets/images/authbuttons/google_32.png b/vendor/assets/images/authbuttons/google_32.png new file mode 100644 index 00000000..3909e9de Binary files /dev/null and b/vendor/assets/images/authbuttons/google_32.png differ diff --git a/vendor/assets/images/authbuttons/google_64.png b/vendor/assets/images/authbuttons/google_64.png new file mode 100644 index 00000000..e55f34f1 Binary files /dev/null and b/vendor/assets/images/authbuttons/google_64.png differ diff --git a/vendor/assets/images/authbuttons/twitter_32.png b/vendor/assets/images/authbuttons/twitter_32.png new file mode 100644 index 00000000..daadcffd Binary files /dev/null and b/vendor/assets/images/authbuttons/twitter_32.png differ diff --git a/vendor/assets/images/authbuttons/twitter_64.png b/vendor/assets/images/authbuttons/twitter_64.png new file mode 100644 index 00000000..68b74530 Binary files /dev/null and b/vendor/assets/images/authbuttons/twitter_64.png differ