BigW Consortium Gitlab

application_controller.rb 11.2 KB
Newer Older
1
require 'gon'
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
  include WorkhorseHelper
10

11
  before_action :authenticate_user_from_private_token!
12
  before_action :authenticate_user!
13
  before_action :validate_user_service_ticket!
14 15
  before_action :reject_blocked!
  before_action :check_password_expiration
16
  before_action :check_2fa_requirement
17
  before_action :ldap_security_check
18
  before_action :sentry_context
19 20 21 22
  before_action :default_headers
  before_action :add_gon_variables
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :require_email, unless: :devise_controller?
23

24
  protect_from_forgery with: :exception
25

26
  helper_method :abilities, :can?, :current_application_settings
27
  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?, :gitlab_project_import_enabled?
gitlabhq committed
28

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

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

39 40 41 42
  rescue_from Gitlab::Access::AccessDeniedError do |exception|
    render_403
  end

43 44 45 46
  def redirect_back_or_default(default: root_path, options: {})
    redirect_to request.referer.present? ? :back : default, options
  end

Nihad Abbasov committed
47
  protected
gitlabhq committed
48

49 50 51 52 53 54 55 56 57
  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
58 59 60 61 62 63 64 65 66 67

      Raven.tags_context(program: sentry_program_context)
    end
  end

  def sentry_program_context
    if Sidekiq.server?
      'sidekiq'
    else
      'rails'
68 69 70
    end
  end

71 72 73 74 75
  # This filter handles both private tokens and personal access tokens
  def authenticate_user_from_private_token!
    token_string = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
    user = User.find_by_authentication_token(token_string) || User.find_by_personal_access_token(token_string)

76 77 78 79 80 81 82 83 84
    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

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

90
    super(*args)
91 92
  end

Riyad Preukschas committed
93 94 95 96 97 98
  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

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

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

117
  def after_sign_out_path_for(resource)
118
    current_application_settings.after_sign_out_path.presence || new_user_session_path
119 120
  end

gitlabhq committed
121
  def abilities
Ciro Santilli committed
122
    Ability.abilities
gitlabhq committed
123 124 125 126 127 128
  end

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

gitlabhq committed
129
  def access_denied!
130
    render "errors/access_denied", layout: "errors", status: 404
131 132 133
  end

  def git_not_found!
134
    render "errors/git_not_found.html", layout: "errors", status: 404
gitlabhq committed
135 136
  end

137 138
  def render_403
    head :forbidden
gitlabhq committed
139
  end
gitlabhq committed
140

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

145 146 147 148 149
  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
150

151 152 153
  def default_headers
    headers['X-Frame-Options'] = 'DENY'
    headers['X-XSS-Protection'] = '1; mode=block'
154
    headers['X-UA-Compatible'] = 'IE=edge'
155
    headers['X-Content-Type-Options'] = 'nosniff'
156 157 158 159
    # 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
160
  end
161

162 163 164 165 166 167 168 169 170 171 172 173 174 175
  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

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

182
  def check_2fa_requirement
183 184
    if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled? && !skip_two_factor?
      redirect_to profile_two_factor_auth_path
185 186 187
    end
  end

188
  def ldap_security_check
189
    if current_user && current_user.requires_ldap_check?
Jacob Vosmaer committed
190
      return unless current_user.try_obtain_ldap_lease
191

192 193 194 195
      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
196 197 198 199
      end
    end
  end

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

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

209 210 211 212 213 214 215 216 217 218 219 220 221
  # 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
222

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

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

  def hexdigest(string)
    Digest::SHA1.hexdigest string
  end
239 240 241 242 243 244

  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
245

246
  def set_filters_params
247 248
    set_default_sort

249 250 251
    params[:scope] = 'all' if params[:scope].blank?
    params[:state] = 'opened' if params[:state].blank?

252
    @sort = params[:sort]
253
    @filter_params = params.dup
254 255

    if @project
256
      @filter_params[:project_id] = @project.id
257
    elsif @group
258
      @filter_params[:group_id] = @group.id
259
    else
260 261 262 263
      # 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
264
      # @filter_params[:authorized_only] = true
265
    end
266 267

    @filter_params
268 269
  end

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

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

282 283 284 285
  def import_sources_enabled?
    !current_application_settings.import_sources.empty?
  end

286
  def github_import_enabled?
287 288 289 290
    current_application_settings.import_sources.include?('github')
  end

  def github_import_configured?
291
    Gitlab::OAuth::Provider.enabled?(:github)
292 293 294
  end

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

  def gitlab_import_configured?
299
    Gitlab::OAuth::Provider.enabled?(:gitlab)
300 301 302
  end

  def bitbucket_import_enabled?
303 304 305 306
    current_application_settings.import_sources.include?('bitbucket')
  end

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

  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 committed
318 319 320 321
  def fogbugz_import_enabled?
    current_application_settings.import_sources.include?('fogbugz')
  end

322 323 324
  def git_import_enabled?
    current_application_settings.import_sources.include?('git')
  end
325

326 327 328 329
  def gitlab_project_import_enabled?
    current_application_settings.import_sources.include?('gitlab_project')
  end

330 331 332 333
  def two_factor_authentication_required?
    current_application_settings.require_two_factor_authentication
  end

334 335 336 337
  def two_factor_grace_period
    current_application_settings.two_factor_grace_period
  end

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

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

347 348 349 350 351 352 353 354 355 356 357 358
  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
359

360 361 362 363 364 365 366
  # 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

367 368 369
  private

  def set_default_sort
370 371
    key = if is_a_listing_page_for?('issues') || is_a_listing_page_for?('merge_requests')
            'issuable_sort'
372
          end
373

374 375 376 377 378 379 380
    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)
381

382 383 384
    (controller_name == "projects/#{page_type}" && action_name == 'index') ||
    (controller_name == 'groups' && action_name == page_type) ||
    (controller_name == 'dashboard' && action_name == page_type)
385
  end
gitlabhq committed
386
end