summaryrefslogtreecommitdiff
path: root/app/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/application_controller.rb17
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb59
-rw-r--r--app/controllers/concerns/toggle_award_emoji.rb22
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb45
-rw-r--r--app/controllers/projects/artifacts_controller.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb16
-rw-r--r--app/controllers/projects/merge_requests_controller.rb11
-rw-r--r--app/controllers/projects/notes_controller.rb39
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb36
12 files changed, 182 insertions, 71 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 9b2a9d298b3..62f63701799 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -182,8 +182,8 @@ class ApplicationController < ActionController::Base
end
def check_2fa_requirement
- if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled && !skip_two_factor?
- redirect_to new_profile_two_factor_auth_path
+ if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled? && !skip_two_factor?
+ redirect_to profile_two_factor_auth_path
end
end
@@ -232,7 +232,7 @@ class ApplicationController < ActionController::Base
end
def configure_permitted_parameters
- devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me, :otp_attempt) }
+ devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :email, :password, :login, :remember_me, :otp_attempt])
end
def hexdigest(string)
@@ -342,6 +342,10 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current
end
+ def browser_supports_u2f?
+ browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
+ end
+
def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page
# Don't redirect to the default URL to prevent endless redirections
@@ -355,6 +359,13 @@ class ApplicationController < ActionController::Base
current_user.nil? && root_path == request.path
end
+ # U2F (universal 2nd factor) devices need a unique identifier for the application
+ # to perform authentication.
+ # https://developers.yubico.com/U2F/App_ID.html
+ def u2f_app_id
+ request.base_url
+ end
+
private
def set_default_sort
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index d5918a7af3b..998b8adc411 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -24,7 +24,64 @@ module AuthenticatesWithTwoFactor
# Returns nil
def prompt_for_two_factor(user)
session[:otp_user_id] = user.id
+ setup_u2f_authentication(user)
+ render 'devise/sessions/two_factor'
+ end
+
+ def authenticate_with_two_factor
+ user = self.resource = find_user
+
+ if user_params[:otp_attempt].present? && session[:otp_user_id]
+ authenticate_with_two_factor_via_otp(user)
+ elsif user_params[:device_response].present? && session[:otp_user_id]
+ authenticate_with_two_factor_via_u2f(user)
+ elsif user && user.valid_password?(user_params[:password])
+ prompt_for_two_factor(user)
+ end
+ end
+
+ private
+
+ def authenticate_with_two_factor_via_otp(user)
+ if valid_otp_attempt?(user)
+ # Remove any lingering user data from login
+ session.delete(:otp_user_id)
+
+ remember_me(user) if user_params[:remember_me] == '1'
+ sign_in(user)
+ else
+ flash.now[:alert] = 'Invalid two-factor code.'
+ render :two_factor
+ end
+ end
+
+ # Authenticate using the response from a U2F (universal 2nd factor) device
+ def authenticate_with_two_factor_via_u2f(user)
+ if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges])
+ # Remove any lingering user data from login
+ session.delete(:otp_user_id)
+ session.delete(:challenges)
+
+ sign_in(user)
+ else
+ flash.now[:alert] = 'Authentication via U2F device failed.'
+ prompt_for_two_factor(user)
+ end
+ end
+
+ # Setup in preparation of communication with a U2F (universal 2nd factor) device
+ # Actual communication is performed using a Javascript API
+ def setup_u2f_authentication(user)
+ key_handles = user.u2f_registrations.pluck(:key_handle)
+ u2f = U2F::U2F.new(u2f_app_id)
- render 'devise/sessions/two_factor' and return
+ if key_handles.present?
+ sign_requests = u2f.authentication_requests(key_handles)
+ challenges = sign_requests.map(&:challenge)
+ session[:challenges] = challenges
+ gon.push(u2f: { challenges: challenges, app_id: u2f_app_id,
+ sign_requests: sign_requests,
+ browser_supports_u2f: browser_supports_u2f? })
+ end
end
end
diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb
new file mode 100644
index 00000000000..09ff44f291b
--- /dev/null
+++ b/app/controllers/concerns/toggle_award_emoji.rb
@@ -0,0 +1,22 @@
+module ToggleAwardEmoji
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :authenticate_user!, only: [:toggle_award_emoji]
+ end
+
+ def toggle_award_emoji
+ name = params.require(:name)
+
+ awardable.toggle_award_emoji(name, current_user)
+ TodoService.new.new_award_emoji(awardable, current_user)
+
+ render json: { ok: true }
+ end
+
+ private
+
+ def awardable
+ raise NotImplementedError
+ end
+end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 8f83fdd02bc..6a358fdcc05 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -1,7 +1,7 @@
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
skip_before_action :check_2fa_requirement
- def new
+ def show
unless current_user.otp_secret
current_user.otp_secret = User.generate_otp_secret(32)
end
@@ -12,21 +12,22 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
current_user.save! if current_user.changed?
- if two_factor_authentication_required?
+ if two_factor_authentication_required? && !current_user.two_factor_enabled?
if two_factor_grace_period_expired?
- flash.now[:alert] = 'You must enable Two-factor Authentication for your account.'
+ flash.now[:alert] = 'You must enable Two-Factor Authentication for your account.'
else
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
- flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}."
+ flash.now[:alert] = "You must enable Two-Factor Authentication for your account before #{l(grace_period_deadline)}."
end
end
@qr_code = build_qr_code
+ setup_u2f_registration
end
def create
if current_user.validate_and_consume_otp!(params[:pin_code])
- current_user.two_factor_enabled = true
+ current_user.otp_required_for_login = true
@codes = current_user.generate_otp_backup_codes!
current_user.save!
@@ -34,8 +35,23 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
else
@error = 'Invalid pin code'
@qr_code = build_qr_code
+ setup_u2f_registration
+ render 'show'
+ end
+ end
+
+ # A U2F (universal 2nd factor) device's information is stored after successful
+ # registration, which is then used while 2FA authentication is taking place.
+ def create_u2f
+ @u2f_registration = U2fRegistration.register(current_user, u2f_app_id, params[:device_response], session[:challenges])
- render 'new'
+ if @u2f_registration.persisted?
+ session.delete(:challenges)
+ redirect_to profile_account_path, notice: "Your U2F device was registered!"
+ else
+ @qr_code = build_qr_code
+ setup_u2f_registration
+ render :show
end
end
@@ -70,4 +86,21 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def issuer_host
Gitlab.config.gitlab.host
end
+
+ # Setup in preparation of communication with a U2F (universal 2nd factor) device
+ # Actual communication is performed using a Javascript API
+ def setup_u2f_registration
+ @u2f_registration ||= U2fRegistration.new
+ @registration_key_handles = current_user.u2f_registrations.pluck(:key_handle)
+ u2f = U2F::U2F.new(u2f_app_id)
+
+ registration_requests = u2f.registration_requests
+ sign_requests = u2f.authentication_requests(@registration_key_handles)
+ session[:challenges] = registration_requests.map(&:challenge)
+
+ gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
+ register_requests: registration_requests,
+ sign_requests: sign_requests,
+ browser_supports_u2f: browser_supports_u2f? })
+ end
end
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index cfea1266516..832d7deb57d 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -37,7 +37,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
private
def build
- @build ||= project.builds.unscoped.find_by!(id: params[:build_id])
+ @build ||= project.builds.find_by!(id: params[:build_id])
end
def artifacts_file
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index d09e7375b67..dd9508da049 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -50,7 +50,7 @@ class Projects::BranchesController < Projects::ApplicationController
redirect_to namespace_project_branches_path(@project.namespace,
@project), status: 303
end
- format.js { render status: status[:return_code] }
+ format.js { render nothing: true, status: status[:return_code] }
end
end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index bb1f6c5e980..db3ae586059 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -81,7 +81,7 @@ class Projects::BuildsController < Projects::ApplicationController
private
def build
- @build ||= project.builds.unscoped.find_by!(id: params[:id])
+ @build ||= project.builds.find_by!(id: params[:id])
end
def build_path(build)
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 016f5dd0005..4e2d3bebb2e 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -1,6 +1,7 @@
class Projects::IssuesController < Projects::ApplicationController
include ToggleSubscriptionAction
include IssuableActions
+ include ToggleAwardEmoji
before_action :module_enabled
before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
@@ -62,7 +63,7 @@ class Projects::IssuesController < Projects::ApplicationController
def show
@note = @project.notes.new(noteable: @issue)
- @notes = @issue.notes.nonawards.with_associations.fresh
+ @notes = @issue.notes.with_associations.fresh
@noteable = @issue
respond_to do |format|
@@ -155,7 +156,12 @@ class Projects::IssuesController < Projects::ApplicationController
def bulk_update
result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute
- redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" })
+
+ respond_to do |format|
+ format.json do
+ render json: { notice: "#{result[:count]} issues updated" }
+ end
+ end
end
protected
@@ -169,6 +175,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
+ alias_method :awardable, :issue
def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue)
@@ -214,7 +221,10 @@ class Projects::IssuesController < Projects::ApplicationController
:issues_ids,
:assignee_id,
:milestone_id,
- :state_event
+ :state_event,
+ label_ids: [],
+ add_label_ids: [],
+ remove_label_ids: []
)
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index d54284d7b20..f78b429b3e7 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -2,6 +2,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction
include DiffHelper
include IssuableActions
+ include ToggleAwardEmoji
before_action :module_enabled
before_action :merge_request, only: [
@@ -190,13 +191,18 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return
end
+ if params[:sha] != @merge_request.source_sha
+ @status = :sha_mismatch
+ return
+ end
+
TodoService.new.merge_merge_request(merge_request, current_user)
@merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
- .execute(@merge_request)
+ .execute(@merge_request)
@status = :merge_when_build_succeeds
else
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
@@ -265,6 +271,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
alias_method :subscribable_resource, :merge_request
alias_method :issuable, :merge_request
+ alias_method :awardable, :merge_request
def closes_issues
@closes_issues ||= @merge_request.closes_issues
@@ -300,7 +307,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_show_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request)
- @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh
+ @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
@discussions = @notes.discussions
@noteable = @merge_request
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 40b24d550e0..c205474e999 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController
before_action :authorize_read_note!
before_action :authorize_create_note!, only: [:create]
before_action :authorize_admin_note!, only: [:update, :destroy]
- before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]
+ before_action :find_current_user_notes, only: [:index]
def index
current_fetched_at = Time.now.to_i
@@ -56,30 +56,6 @@ class Projects::NotesController < Projects::ApplicationController
end
end
- def award_toggle
- noteable = if note_params[:noteable_type] == "issue"
- project.issues.find(note_params[:noteable_id])
- else
- project.merge_requests.find(note_params[:noteable_id])
- end
-
- data = {
- author: current_user,
- is_award: true,
- note: note_params[:note].delete(":")
- }
-
- note = noteable.notes.find_by(data)
-
- if note
- note.destroy
- else
- Notes::CreateService.new(project, current_user, note_params).execute
- end
-
- render json: { ok: true }
- end
-
private
def note
@@ -131,13 +107,20 @@ class Projects::NotesController < Projects::ApplicationController
end
def note_json(note)
- if note.valid?
+ if note.is_a?(AwardEmoji)
+ {
+ valid: note.valid?,
+ award: true,
+ id: note.id,
+ name: note.name
+ }
+ elsif note.valid?
{
valid: true,
id: note.id,
discussion_id: note.discussion_id,
html: note_to_html(note),
- award: note.is_award,
+ award: false,
note: note.note,
discussion_html: note_to_discussion_html(note),
discussion_with_diff_html: note_to_discussion_with_diff_html(note)
@@ -145,7 +128,7 @@ class Projects::NotesController < Projects::ApplicationController
else
{
valid: false,
- award: note.is_award,
+ award: false,
errors: note.errors
}
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index f94e2a84fa2..3af62c7696c 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -139,7 +139,7 @@ class ProjectsController < Projects::ApplicationController
participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id)
@suggestions = {
- emojis: AwardEmoji.urls,
+ emojis: Gitlab::AwardEmoji.urls,
issues: autocomplete.issues,
milestones: autocomplete.milestones,
mergerequests: autocomplete.merge_requests,
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index d68c2a708e3..f6eedb1773c 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -30,8 +30,7 @@ class SessionsController < Devise::SessionsController
resource.update_attributes(reset_password_token: nil,
reset_password_sent_at: nil)
end
- authenticated_with = user_params[:otp_attempt] ? "two-factor" : "standard"
- log_audit_event(current_user, with: authenticated_with)
+ log_audit_event(current_user, with: authentication_method)
end
end
@@ -54,7 +53,7 @@ class SessionsController < Devise::SessionsController
end
def user_params
- params.require(:user).permit(:login, :password, :remember_me, :otp_attempt)
+ params.require(:user).permit(:login, :password, :remember_me, :otp_attempt, :device_response)
end
def find_user
@@ -89,27 +88,6 @@ class SessionsController < Devise::SessionsController
find_user.try(:two_factor_enabled?)
end
- def authenticate_with_two_factor
- user = self.resource = find_user
-
- if user_params[:otp_attempt].present? && session[:otp_user_id]
- if valid_otp_attempt?(user)
- # Remove any lingering user data from login
- session.delete(:otp_user_id)
-
- remember_me(user) if user_params[:remember_me] == '1'
- sign_in(user) and return
- else
- flash.now[:alert] = 'Invalid two-factor code.'
- render :two_factor and return
- end
- else
- if user && user.valid_password?(user_params[:password])
- prompt_for_two_factor(user)
- end
- end
- end
-
def auto_sign_in_with_provider
provider = Gitlab.config.omniauth.auto_sign_in_with_provider
return unless provider.present?
@@ -138,4 +116,14 @@ class SessionsController < Devise::SessionsController
def load_recaptcha
Gitlab::Recaptcha.load_configurations!
end
+
+ def authentication_method
+ if user_params[:otp_attempt]
+ "two-factor"
+ elsif user_params[:device_response]
+ "two-factor-via-u2f-device"
+ else
+ "standard"
+ end
+ end
end