application_controller.rb 11.1 KB
Newer Older
1
require 'gon'
Jared Szechy's avatar
Jared Szechy committed
2
require 'fogbugz'
3

4
class ApplicationController < ActionController::Base
5
  include Gitlab::CurrentSettings
6
  include Gitlab::GonHelper
7 8
  include GitlabRoutingHelper
  include PageLayoutHelper
9

10 11
  before_action :authenticate_user_from_token!
  before_action :authenticate_user!
tduehr's avatar
tduehr committed
12
  before_action :validate_user_service_ticket!
13 14
  before_action :reject_blocked!
  before_action :check_password_expiration
15
  before_action :check_2fa_requirement
16
  before_action :ldap_security_check
17
  before_action :sentry_context
18 19 20 21
  before_action :default_headers
  before_action :add_gon_variables
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :require_email, unless: :devise_controller?
22

23
  protect_from_forgery with: :exception
24

25
  helper_method :abilities, :can?, :current_application_settings
Jared Szechy's avatar
Jared Szechy committed
26
  helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
gitlabhq's avatar
gitlabhq committed
27

28
  rescue_from Encoding::CompatibilityError do |exception|
Riyad Preukschas's avatar
Riyad Preukschas committed
29
    log_exception(exception)
30
    render "errors/encoding", layout: "errors", status: 500
31 32
  end

33
  rescue_from ActiveRecord::RecordNotFound do |exception|
Riyad Preukschas's avatar
Riyad Preukschas committed
34
    log_exception(exception)
35
    render_404
gitlabhq's avatar
gitlabhq committed
36 37
  end

38 39 40 41
  def redirect_back_or_default(default: root_path, options: {})
    redirect_to request.referer.present? ? :back : default, options
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
42
  protected
gitlabhq's avatar
gitlabhq committed
43

44 45 46 47 48 49 50 51 52
  def sentry_context
    if Rails.env.production? && current_application_settings.sentry_enabled
      if current_user
        Raven.user_context(
          id: current_user.id,
          email: current_user.email,
          username: current_user.username,
        )
      end
53 54 55 56 57 58 59 60 61 62

      Raven.tags_context(program: sentry_program_context)
    end
  end

  def sentry_program_context
    if Sidekiq.server?
      'sidekiq'
    else
      'rails'
Douwe Maan's avatar
Douwe Maan committed
63 64 65
    end
  end

66
  # From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
67
  # https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
68
  def authenticate_user_from_token!
69 70 71 72
    user_token = if params[:authenticity_token].presence
                   params[:authenticity_token].presence
                 elsif params[:private_token].presence
                   params[:private_token].presence
73 74
                 elsif request.headers['PRIVATE-TOKEN'].present?
                   request.headers['PRIVATE-TOKEN']
75
                 end
76 77 78 79 80 81 82 83 84 85 86
    user = user_token && User.find_by_authentication_token(user_token.to_s)

    if user
      # Notice we are passing store false, so the user is not
      # actually stored in the session and a token is needed
      # for every request. If you want the token to work as a
      # sign in token, you can simply remove store: false.
      sign_in user, store: false
    end
  end

87
  def authenticate_user!(*args)
88 89
    if redirect_to_home_page_url?
      redirect_to current_application_settings.home_page_url and return
90 91
    end

92
    super(*args)
93 94
  end

Riyad Preukschas's avatar
Riyad Preukschas committed
95 96 97 98 99 100
  def log_exception(exception)
    application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
    application_trace.map!{ |t| "  #{t}\n" }
    logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
  end

101
  def reject_blocked!
102
    if current_user && current_user.blocked?
103
      sign_out current_user
104
      flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
105 106 107 108
      redirect_to new_user_session_path
    end
  end

109
  def after_sign_in_path_for(resource)
110
    if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
randx's avatar
randx committed
111
      sign_out resource
112
      flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
randx's avatar
randx committed
113 114
      new_user_session_path
    else
115
      stored_location_for(:redirect) || stored_location_for(resource) || root_path
randx's avatar
randx committed
116 117 118
    end
  end

119
  def after_sign_out_path_for(resource)
120
    current_application_settings.after_sign_out_path.presence || new_user_session_path
121 122
  end

gitlabhq's avatar
gitlabhq committed
123
  def abilities
Ciro Santilli's avatar
Ciro Santilli committed
124
    Ability.abilities
gitlabhq's avatar
gitlabhq committed
125 126 127 128 129 130
  end

  def can?(object, action, subject)
    abilities.allowed?(object, action, subject)
  end

gitlabhq's avatar
gitlabhq committed
131
  def access_denied!
132
    render "errors/access_denied", layout: "errors", status: 404
133 134 135
  end

  def git_not_found!
136
    render "errors/git_not_found.html", layout: "errors", status: 404
gitlabhq's avatar
gitlabhq committed
137 138
  end

139 140
  def render_403
    head :forbidden
gitlabhq's avatar
gitlabhq committed
141
  end
gitlabhq's avatar
gitlabhq committed
142

143 144
  def render_404
    render file: Rails.root.join("public", "404"), layout: false, status: "404"
145 146
  end

147 148 149 150 151
  def no_cache_headers
    response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
  end
152

153 154 155
  def default_headers
    headers['X-Frame-Options'] = 'DENY'
    headers['X-XSS-Protection'] = '1; mode=block'
156
    headers['X-UA-Compatible'] = 'IE=edge'
157
    headers['X-Content-Type-Options'] = 'nosniff'
158 159 160 161
    # Enabling HSTS for non-standard ports would send clients to the wrong port
    if Gitlab.config.gitlab.https and Gitlab.config.gitlab.port == 443
      headers['Strict-Transport-Security'] = 'max-age=31536000'
    end
162
  end
163

tduehr's avatar
tduehr committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177
  def validate_user_service_ticket!
    return unless signed_in? && session[:service_tickets]

    valid = session[:service_tickets].all? do |provider, ticket|
      Gitlab::OAuth::Session.valid?(provider, ticket)
    end

    unless valid
      session[:service_tickets] = nil
      sign_out current_user
      redirect_to new_user_session_path
    end
  end

178
  def check_password_expiration
179
    if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
180 181 182
      redirect_to new_profile_password_path and return
    end
  end
183

184
  def check_2fa_requirement
185
    if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled && !skip_two_factor?
186
      redirect_to new_profile_two_factor_auth_path
187 188 189
    end
  end

190
  def ldap_security_check
191
    if current_user && current_user.requires_ldap_check?
Jacob Vosmaer's avatar
Jacob Vosmaer committed
192
      return unless current_user.try_obtain_ldap_lease
193

194 195 196 197
      unless Gitlab::LDAP::Access.allowed?(current_user)
        sign_out current_user
        flash[:alert] = "Access denied for your LDAP account."
        redirect_to new_user_session_path
198 199 200 201
      end
    end
  end

202 203 204 205
  def event_filter
    filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
    @event_filter ||= EventFilter.new(filters)
  end
206

207 208
  def gitlab_ldap_access(&block)
    Gitlab::LDAP::Access.open { |access| block.call(access) }
209 210
  end

211 212 213 214 215 216 217 218 219 220 221 222 223
  # JSON for infinite scroll via Pager object
  def pager_json(partial, count)
    html = render_to_string(
      partial,
      layout: false,
      formats: [:html]
    )

    render json: {
      html: html,
      count: count
    }
  end
224

Josh Frye's avatar
Josh Frye committed
225
  def view_to_html_string(partial, locals = {})
226
    render_to_string(
Josh Frye's avatar
Josh Frye committed
227
      partial,
228
      locals: locals,
229 230 231 232
      layout: false,
      formats: [:html]
    )
  end
233 234

  def configure_permitted_parameters
235
    devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :email, :password, :login, :remember_me, :otp_attempt])
236
  end
237 238 239 240

  def hexdigest(string)
    Digest::SHA1.hexdigest string
  end
241 242 243 244 245 246

  def require_email
    if current_user && current_user.temp_oauth_email?
      redirect_to profile_path, notice: 'Please complete your profile with email address' and return
    end
  end
247

248
  def set_filters_params
249 250
    set_default_sort

251 252 253
    params[:scope] = 'all' if params[:scope].blank?
    params[:state] = 'opened' if params[:state].blank?

254
    @sort = params[:sort]
255
    @filter_params = params.dup
256 257

    if @project
258
      @filter_params[:project_id] = @project.id
259
    elsif @group
260
      @filter_params[:group_id] = @group.id
261
    else
262 263 264 265 266
      # TODO: this filter ignore issues/mr created in public or
      # internal repos where you are not a member. Enable this filter
      # or improve current implementation to filter only issues you
      # created or assigned or mentioned
      #@filter_params[:authorized_only] = true
267
    end
268 269

    @filter_params
270 271
  end

272 273
  def get_issues_collection
    set_filters_params
274 275
    @issuable_finder = IssuesFinder.new(current_user, @filter_params)
    @issuable_finder.execute
276 277 278 279
  end

  def get_merge_requests_collection
    set_filters_params
280 281
    @issuable_finder = MergeRequestsFinder.new(current_user, @filter_params)
    @issuable_finder.execute
282
  end
283

284 285 286 287
  def import_sources_enabled?
    !current_application_settings.import_sources.empty?
  end

288
  def github_import_enabled?
289 290 291 292
    current_application_settings.import_sources.include?('github')
  end

  def github_import_configured?
293
    Gitlab::OAuth::Provider.enabled?(:github)
294 295 296
  end

  def gitlab_import_enabled?
297 298 299 300
    request.host != 'gitlab.com' && current_application_settings.import_sources.include?('gitlab')
  end

  def gitlab_import_configured?
301
    Gitlab::OAuth::Provider.enabled?(:gitlab)
302 303 304
  end

  def bitbucket_import_enabled?
305 306 307 308
    current_application_settings.import_sources.include?('bitbucket')
  end

  def bitbucket_import_configured?
309
    Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
310
  end
311 312 313 314 315 316 317 318 319

  def gitorious_import_enabled?
    current_application_settings.import_sources.include?('gitorious')
  end

  def google_code_import_enabled?
    current_application_settings.import_sources.include?('google_code')
  end

Jared Szechy's avatar
Jared Szechy committed
320 321 322 323
  def fogbugz_import_enabled?
    current_application_settings.import_sources.include?('fogbugz')
  end

324 325 326
  def git_import_enabled?
    current_application_settings.import_sources.include?('git')
  end
327

328 329 330 331
  def two_factor_authentication_required?
    current_application_settings.require_two_factor_authentication
  end

332 333 334 335
  def two_factor_grace_period
    current_application_settings.two_factor_grace_period
  end

336 337
  def two_factor_grace_period_expired?
    date = current_user.otp_grace_period_started_at
338 339 340 341 342 343 344
    date && (date + two_factor_grace_period.hours) < Time.current
  end

  def skip_two_factor?
    session[:skip_tfa] && session[:skip_tfa] > Time.current
  end

345 346 347 348 349 350 351 352 353 354 355 356
  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
    return false unless current_application_settings.home_page_url.present?

    home_page_url = current_application_settings.home_page_url.chomp('/')
    root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]

    return false if root_urls.include?(home_page_url)

    current_user.nil? && root_path == request.path
  end
357 358 359 360

  private

  def set_default_sort
361 362
    key = if is_a_listing_page_for?('issues') || is_a_listing_page_for?('merge_requests')
            'issuable_sort'
363
          end
364

365 366 367 368 369 370 371
    cookies[key]  = params[:sort] if key && params[:sort].present?
    params[:sort] = cookies[key] if key
    params[:sort] ||= 'id_desc'
  end

  def is_a_listing_page_for?(page_type)
    controller_name, action_name = params.values_at(:controller, :action)
372

373 374 375
    (controller_name == "projects/#{page_type}" && action_name == 'index') ||
    (controller_name == 'groups' && action_name == page_type) ||
    (controller_name == 'dashboard' && action_name == page_type)
376
  end
gitlabhq's avatar
gitlabhq committed
377
end