application_controller.rb 10.1 KB
Newer Older
1 2
require 'gon'

3
class ApplicationController < ActionController::Base
4
  include Gitlab::CurrentSettings
5
  include GitlabRoutingHelper
6
  include PageLayoutHelper
7

8 9
  PER_PAGE = 20

10 11 12 13 14 15 16 17 18
  before_action :authenticate_user_from_token!
  before_action :authenticate_user!
  before_action :reject_blocked!
  before_action :check_password_expiration
  before_action :ldap_security_check
  before_action :default_headers
  before_action :add_gon_variables
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :require_email, unless: :devise_controller?
19

20
  protect_from_forgery with: :exception
21

22
  helper_method :abilities, :can?, :current_application_settings
23
  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?, :git_import_enabled?
gitlabhq's avatar
gitlabhq committed
24

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

30
  rescue_from ActiveRecord::RecordNotFound do |exception|
Riyad Preukschas's avatar
Riyad Preukschas committed
31
    log_exception(exception)
Cyril's avatar
Cyril committed
32
    render "errors/not_found", layout: "errors", status: 404
gitlabhq's avatar
gitlabhq committed
33 34
  end

Nihad Abbasov's avatar
Nihad Abbasov committed
35
  protected
gitlabhq's avatar
gitlabhq committed
36

37
  # From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
38
  # https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
39
  def authenticate_user_from_token!
40 41 42 43 44
    user_token = if params[:authenticity_token].presence
                   params[:authenticity_token].presence
                 elsif params[:private_token].presence
                   params[:private_token].presence
                 end
45 46 47 48 49 50 51 52 53 54 55
    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

56
  def authenticate_user!(*args)
Steven Burgart's avatar
Steven Burgart committed
57
    # If user is not signed-in and tries to access root_path - redirect him to landing page
58 59 60
    # Don't redirect to the default URL to prevent endless redirections
    if current_application_settings.home_page_url.present? &&
        current_application_settings.home_page_url.chomp('/') != Gitlab.config.gitlab['url'].chomp('/')
61
      if current_user.nil? && root_path == request.path
62 63 64 65
        redirect_to current_application_settings.home_page_url and return
      end
    end

66
    super(*args)
67 68
  end

Riyad Preukschas's avatar
Riyad Preukschas committed
69 70 71 72 73 74
  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

75
  def reject_blocked!
76
    if current_user && current_user.blocked?
77
      sign_out current_user
78
      flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
79 80 81 82
      redirect_to new_user_session_path
    end
  end

83
  def after_sign_in_path_for(resource)
84
    if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
randx's avatar
randx committed
85
      sign_out resource
86
      flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
randx's avatar
randx committed
87 88
      new_user_session_path
    else
89
      stored_location_for(:redirect) || stored_location_for(resource) || root_path
randx's avatar
randx committed
90 91 92
    end
  end

93
  def after_sign_out_path_for(resource)
94
    current_application_settings.after_sign_out_path || new_user_session_path
95 96
  end

gitlabhq's avatar
gitlabhq committed
97
  def abilities
Ciro Santilli's avatar
Ciro Santilli committed
98
    Ability.abilities
gitlabhq's avatar
gitlabhq committed
99 100 101 102 103 104
  end

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

Nihad Abbasov's avatar
Nihad Abbasov committed
105
  def project
106
    unless @project
Vinnie Okada's avatar
Vinnie Okada committed
107
      namespace = params[:namespace_id]
108 109 110 111 112 113 114 115 116 117 118
      id = params[:project_id] || params[:id]

      # Redirect from
      #   localhost/group/project.git
      # to
      #   localhost/group/project
      #
      if id =~ /\.git\Z/
        redirect_to request.original_url.gsub(/\.git\Z/, '') and return
      end

Vinnie Okada's avatar
Vinnie Okada committed
119
      @project = Project.find_with_namespace("#{namespace}/#{id}")
120 121 122 123 124 125 126 127 128 129

      if @project and can?(current_user, :read_project, @project)
        @project
      elsif current_user.nil?
        @project = nil
        authenticate_user!
      else
        @project = nil
        render_404 and return
      end
130
    end
131
    @project
gitlabhq's avatar
gitlabhq committed
132 133
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
134 135
  def repository
    @repository ||= project.repository
136
  rescue Grit::NoSuchPathError => e
Vinnie Okada's avatar
Vinnie Okada committed
137
    log_exception(e)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
138 139 140
    nil
  end

gitlabhq's avatar
gitlabhq committed
141
  def authorize_project!(action)
142
    return access_denied! unless can?(current_user, action, project)
gitlabhq's avatar
gitlabhq committed
143 144 145
  end

  def access_denied!
Cyril's avatar
Cyril committed
146
    render "errors/access_denied", layout: "errors", status: 404
147 148 149
  end

  def not_found!
Cyril's avatar
Cyril committed
150
    render "errors/not_found", layout: "errors", status: 404
151 152 153
  end

  def git_not_found!
Cyril's avatar
Cyril committed
154
    render "errors/git_not_found", layout: "errors", status: 404
gitlabhq's avatar
gitlabhq committed
155 156 157
  end

  def method_missing(method_sym, *arguments, &block)
158
    if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
gitlabhq's avatar
gitlabhq committed
159 160 161 162 163
      authorize_project!($1.to_sym)
    else
      super
    end
  end
gitlabhq's avatar
gitlabhq committed
164

165 166
  def render_403
    head :forbidden
gitlabhq's avatar
gitlabhq committed
167
  end
gitlabhq's avatar
gitlabhq committed
168

169 170
  def render_404
    render file: Rails.root.join("public", "404"), layout: false, status: "404"
171 172
  end

gitlabhq's avatar
gitlabhq committed
173
  def require_non_empty_project
174
    redirect_to @project if @project.empty_repo?
gitlabhq's avatar
gitlabhq committed
175
  end
176

177 178 179 180 181
  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
182

183 184 185
  def default_headers
    headers['X-Frame-Options'] = 'DENY'
    headers['X-XSS-Protection'] = '1; mode=block'
xyb's avatar
xyb committed
186
    headers['X-UA-Compatible'] = 'IE=edge'
187
    headers['X-Content-Type-Options'] = 'nosniff'
188 189 190 191
    # 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
192
  end
193 194

  def add_gon_variables
195 196
    gon.api_version            = API::API.version
    gon.default_avatar_url     = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
197
    gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
198 199 200
    gon.max_file_size          = current_application_settings.max_attachment_size
    gon.relative_url_root      = Gitlab.config.gitlab.relative_url_root
    gon.user_color_scheme      = Gitlab::ColorSchemes.for_user(current_user).css_class
201 202 203 204 205

    if current_user
      gon.current_user_id = current_user.id
      gon.api_token = current_user.private_token
    end
206
  end
207 208

  def check_password_expiration
209
    if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now  && !current_user.ldap_user?
210 211 212
      redirect_to new_profile_password_path and return
    end
  end
213

214
  def ldap_security_check
215
    if current_user && current_user.requires_ldap_check?
216 217 218 219
      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
220 221 222 223
      end
    end
  end

224 225 226 227
  def event_filter
    filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
    @event_filter ||= EventFilter.new(filters)
  end
228

229 230
  def gitlab_ldap_access(&block)
    Gitlab::LDAP::Access.open { |access| block.call(access) }
231 232
  end

233 234 235 236 237 238 239 240 241 242 243 244 245
  # 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
246 247 248 249 250 251 252 253

  def view_to_html_string(partial)
    render_to_string(
      partial,
      layout: false,
      formats: [:html]
    )
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
254 255

  def configure_permitted_parameters
256
    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me, :otp_attempt) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
257
  end
258 259 260 261

  def hexdigest(string)
    Digest::SHA1.hexdigest string
  end
262 263 264 265 266 267

  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
268

269
  def set_filters_params
270
    params[:sort] ||= 'created_desc'
271 272 273
    params[:scope] = 'all' if params[:scope].blank?
    params[:state] = 'opened' if params[:state].blank?

274
    @sort = params[:sort]
275
    @filter_params = params.dup
276 277

    if @project
278
      @filter_params[:project_id] = @project.id
279
    elsif @group
280
      @filter_params[:group_id] = @group.id
281
    else
282 283 284 285 286
      # 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
287
    end
288 289

    @filter_params
290 291
  end

292 293
  def get_issues_collection
    set_filters_params
294 295
    @issuable_finder = IssuesFinder.new(current_user, @filter_params)
    @issuable_finder.execute
296 297 298 299
  end

  def get_merge_requests_collection
    set_filters_params
300 301
    @issuable_finder = MergeRequestsFinder.new(current_user, @filter_params)
    @issuable_finder.execute
302
  end
303

304 305 306 307
  def import_sources_enabled?
    !current_application_settings.import_sources.empty?
  end

308
  def github_import_enabled?
309 310 311 312
    current_application_settings.import_sources.include?('github')
  end

  def github_import_configured?
313
    Gitlab::OAuth::Provider.enabled?(:github)
314 315 316
  end

  def gitlab_import_enabled?
317 318 319 320
    request.host != 'gitlab.com' && current_application_settings.import_sources.include?('gitlab')
  end

  def gitlab_import_configured?
321
    Gitlab::OAuth::Provider.enabled?(:gitlab)
322 323 324
  end

  def bitbucket_import_enabled?
325 326 327 328
    current_application_settings.import_sources.include?('bitbucket')
  end

  def bitbucket_import_configured?
329
    Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
330
  end
331 332 333 334 335 336 337 338 339 340 341 342

  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

  def git_import_enabled?
    current_application_settings.import_sources.include?('git')
  end
gitlabhq's avatar
gitlabhq committed
343
end