diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d4a6d09e4..3858dd11c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -6,7 +6,7 @@ require_dependency 'rate_limiter' class UsersController < ApplicationController skip_before_filter :authorize_mini_profiler, only: [:avatar] - skip_before_filter :check_xhr, only: [:show, :password_reset, :update, :account_created, :activate_account, :perform_account_activation, :authorize_email, :user_preferences_redirect, :avatar, :my_redirect, :toggle_anon] + skip_before_filter :check_xhr, only: [:show, :password_reset, :update, :account_created, :activate_account, :perform_account_activation, :authorize_email, :user_preferences_redirect, :avatar, :my_redirect, :toggle_anon, :admin_login] before_filter :ensure_logged_in, only: [:username, :update, :change_email, :user_preferences_redirect, :upload_user_image, :pick_avatar, :destroy_user_image, :destroy, :check_emails] before_filter :respond_to_suspicious_request, only: [:create] @@ -23,7 +23,8 @@ class UsersController < ApplicationController :perform_account_activation, :send_activation_email, :authorize_email, - :password_reset] + :password_reset, + :admin_login] def index end @@ -344,6 +345,46 @@ class UsersController < ApplicationController @success = I18n.t(message) end + def admin_login + + unless SiteSetting.enable_sso && !current_user + return redirect_to path("/") + end + + if request.put? + RateLimiter.new(nil, "admin-login-hr-#{request.remote_ip}", 6, 1.hour).performed! + RateLimiter.new(nil, "admin-login-min-#{request.remote_ip}", 3, 1.minute).performed! + + user = User.where(email: params[:email], admin: true).where.not(id: Discourse::SYSTEM_USER_ID).first + if user + email_token = user.email_tokens.create(email: user.email) + Jobs.enqueue(:user_email, type: :admin_login, user_id: user.id, email_token: email_token.token) + @message = I18n.t("admin_login.success") + else + @message = I18n.t("admin_login.error") + end + elsif params[:token].present? + # token recieved, try to login + if EmailToken.valid_token_format?(params[:token]) + @user = EmailToken.confirm(params[:token]) + if @user && @user.admin? + # Log in user + log_on_user(@user) + return redirect_to path("/") + else + @message = I18n.t("admin_login.error") + end + else + @message = I18n.t("admin_login.error") + end + end + + render layout: false + rescue RateLimiter::LimitExceeded + @message = I18n.t("rate_limiter.slow_down") + render layout: false + end + def toggle_anon user = AnonymousShadowCreator.get_master(current_user) || AnonymousShadowCreator.get(current_user) diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index 74295249e..ef6e8ae07 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -31,6 +31,12 @@ class UserNotifications < ActionMailer::Base email_token: opts[:email_token]) end + def admin_login(user, opts={}) + build_email( user.email, + template: "user_notifications.admin_login", + email_token: opts[:email_token]) + end + def account_created(user, opts={}) build_email( user.email, template: "user_notifications.account_created", email_token: opts[:email_token]) end diff --git a/app/views/users/admin_login.html.erb b/app/views/users/admin_login.html.erb new file mode 100644 index 000000000..2eaa56e6b --- /dev/null +++ b/app/views/users/admin_login.html.erb @@ -0,0 +1,16 @@ + + + Admin Login + + + <% if @message %> + <%= @message %> + <% else %> + <%=form_tag({}, method: :put) do %> + <%= label_tag(:email, t('admin_login.email_input')) %> + <%= text_field_tag(:email) %>

+ <%= submit_tag t('admin_login.submit_button') %> + <% end %> + <% end %> + + diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 36960de79..e63605de4 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1906,6 +1906,16 @@ en: Click the following link to choose a password: %{base_url}/users/password-reset/%{email_token} + admin_login: + subject_template: "[%{site_name}] Login" + text_body_template: | + Somebody asked to login to your account on [%{site_name}](%{base_url}). + + If you did not make this request, you can safely ignore this email. + + Click the following link to login: + %{base_url}/users/admin-login/%{email_token} + account_created: subject_template: "[%{site_name}] Your New Account" text_body_template: | @@ -2442,3 +2452,9 @@ en: leader: | Blah blah blah Blah blah blah + + admin_login: + success: "Email Sent" + error: "Error!" + email_input: "Admin Email" + submit_button: "Send Email" diff --git a/config/routes.rb b/config/routes.rb index d71ecc3c5..5c68f055c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -229,6 +229,10 @@ Discourse::Application.routes.draw do get "privacy" => "static#show", id: "privacy", as: 'privacy' get "signup" => "list#latest" + get "users/admin-login" => "users#admin_login" + put "users/admin-login" => "users#admin_login" + get "users/admin-login/:token" => "users#admin_login" + post "users/toggle-anon" => "users#toggle_anon" post "users/read-faq" => "users#read_faq" get "users/search/users" => "users#search_users" diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 902ce8fb0..a1b7e4b09 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -315,6 +315,57 @@ describe UsersController do end end + describe '.admin_login' do + let(:admin) { Fabricate(:admin) } + let(:user) { Fabricate(:user) } + + context 'enqueues mail' do + it 'enqueues mail with admin email and sso enabled' do + SiteSetting.enable_sso = true + Jobs.expects(:enqueue).with(:user_email, has_entries(type: :admin_login, user_id: admin.id)) + put :admin_login, email: admin.email + end + + it 'does not enqueue mail with admin email and sso disabled' do + SiteSetting.enable_sso = false + Jobs.expects(:enqueue).never + put :admin_login, email: admin.email + end + + it 'does not enqueue mail with normal user email and sso enabled' do + SiteSetting.enable_sso = true + Jobs.expects(:enqueue).never + put :admin_login, email: user.email + end + end + + context 'logs in admin' do + it 'does not log in admin with invalid token' do + SiteSetting.enable_sso = true + get :admin_login, token: "invalid" + expect(session[:current_user_id]).to be_blank + end + + it 'does not log in admin with valid token and SSO disabled' do + SiteSetting.enable_sso = false + token = admin.email_tokens.create(email: admin.email).token + + get :admin_login, token: token + expect(response).to redirect_to('/') + expect(session[:current_user_id]).to be_blank + end + + it 'logs in admin with valid token and SSO enabled' do + SiteSetting.enable_sso = true + token = admin.email_tokens.create(email: admin.email).token + + get :admin_login, token: token + expect(response).to redirect_to('/') + expect(session[:current_user_id]).to eq(admin.id) + end + end + end + describe '#toggle_anon' do it 'allows you to toggle anon if enabled' do SiteSetting.allow_anonymous_posting = true