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

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

7 8
  PER_PAGE = 20

9 10 11 12 13 14 15 16 17
  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?
18

19
  protect_from_forgery with: :exception
20

21
  helper_method :abilities, :can?, :current_application_settings
Douwe Maan's avatar
Douwe Maan committed
22
  helper_method :github_import_enabled?, :gitlab_import_enabled?, :bitbucket_import_enabled?
gitlabhq's avatar
gitlabhq committed
23

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

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

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

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

55
  def authenticate_user!(*args)
Steven Burgart's avatar
Steven Burgart committed
56
    # If user is not signed-in and tries to access root_path - redirect him to landing page
57 58 59 60 61 62
    if current_application_settings.home_page_url.present?
      if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
        redirect_to current_application_settings.home_page_url and return
      end
    end

63
    super(*args)
64 65
  end

Riyad Preukschas's avatar
Riyad Preukschas committed
66 67 68 69 70 71
  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

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

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

90 91 92 93
  def after_sign_out_path_for(resource)
    new_user_session_path
  end

gitlabhq's avatar
gitlabhq committed
94
  def abilities
Ciro Santilli's avatar
Ciro Santilli committed
95
    Ability.abilities
gitlabhq's avatar
gitlabhq committed
96 97 98 99 100 101
  end

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

Nihad Abbasov's avatar
Nihad Abbasov committed
102
  def project
103
    unless @project
Vinnie Okada's avatar
Vinnie Okada committed
104
      namespace = params[:namespace_id]
105 106 107 108 109 110 111 112 113 114 115
      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
116
      @project = Project.find_with_namespace("#{namespace}/#{id}")
117 118 119 120 121 122 123 124 125 126

      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
127
    end
128
    @project
gitlabhq's avatar
gitlabhq committed
129 130
  end

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

gitlabhq's avatar
gitlabhq committed
138
  def authorize_project!(action)
139
    return access_denied! unless can?(current_user, action, project)
gitlabhq's avatar
gitlabhq committed
140 141
  end

142 143 144 145 146
  def authorize_labels!
    # Labels should be accessible for issues and/or merge requests
    authorize_read_issue! || authorize_read_merge_request!
  end

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

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

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

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

167 168
  def render_403
    head :forbidden
gitlabhq's avatar
gitlabhq committed
169
  end
gitlabhq's avatar
gitlabhq committed
170

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

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

179 180 181 182 183
  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
184

185 186 187
  def default_headers
    headers['X-Frame-Options'] = 'DENY'
    headers['X-XSS-Protection'] = '1; mode=block'
xyb's avatar
xyb committed
188
    headers['X-UA-Compatible'] = 'IE=edge'
189
    headers['X-Content-Type-Options'] = 'nosniff'
190
    headers['Strict-Transport-Security'] = 'max-age=31536000' if Gitlab.config.gitlab.https
191
  end
192 193

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

    if current_user
      gon.current_user_id = current_user.id
      gon.api_token = current_user.private_token
    end
204
  end
205 206

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

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

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

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

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

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

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

  def hexdigest(string)
    Digest::SHA1.hexdigest string
  end
260 261 262 263 264 265

  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
266

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

272
    @filter_params = params.dup
273 274

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

    @filter_params
287 288 289
  end

  def set_filter_values(collection)
290 291 292
    assignee_id = @filter_params[:assignee_id]
    author_id = @filter_params[:author_id]
    milestone_id = @filter_params[:milestone_id]
293

294
    @sort = @filter_params[:sort]
295 296 297 298 299
    @assignees = User.where(id: collection.pluck(:assignee_id))
    @authors = User.where(id: collection.pluck(:author_id))
    @milestones = Milestone.where(id: collection.pluck(:milestone_id))

    if assignee_id.present? && !assignee_id.to_i.zero?
300
      @assignee = @assignees.find_by(id: assignee_id)
301 302 303
    end

    if author_id.present? && !author_id.to_i.zero?
304
      @author = @authors.find_by(id: author_id)
305 306 307
    end

    if milestone_id.present? && !milestone_id.to_i.zero?
308
      @milestone = @milestones.find_by(id: milestone_id)
309 310
    end
  end
311 312 313 314 315 316 317 318 319 320 321 322 323 324

  def get_issues_collection
    set_filters_params
    issues = IssuesFinder.new.execute(current_user, @filter_params)
    set_filter_values(issues)
    issues
  end

  def get_merge_requests_collection
    set_filters_params
    merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params)
    set_filter_values(merge_requests)
    merge_requests
  end
Douwe Maan's avatar
Douwe Maan committed
325 326 327 328 329 330 331 332 333 334 335 336

  def github_import_enabled?
    OauthHelper.enabled_oauth_providers.include?(:github)
  end

  def gitlab_import_enabled?
    OauthHelper.enabled_oauth_providers.include?(:gitlab)
  end

  def bitbucket_import_enabled?
    OauthHelper.enabled_oauth_providers.include?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
  end
gitlabhq's avatar
gitlabhq committed
337
end