application_controller.rb 10 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
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 188 189 190 191 192 193 194 195 196
  def default_url_options
    if !Rails.env.test?
      port = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port?
      { host: Gitlab.config.gitlab.host,
        protocol: Gitlab.config.gitlab.protocol,
        port: port,
        script_name: Gitlab.config.gitlab.relative_url_root }
    else
      super
    end
  end

197 198 199
  def default_headers
    headers['X-Frame-Options'] = 'DENY'
    headers['X-XSS-Protection'] = '1; mode=block'
xyb's avatar
xyb committed
200
    headers['X-UA-Compatible'] = 'IE=edge'
201
    headers['X-Content-Type-Options'] = 'nosniff'
202
    headers['Strict-Transport-Security'] = 'max-age=31536000' if Gitlab.config.gitlab.https
203
  end
204 205

  def add_gon_variables
206
    gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
207
    gon.api_version = API::API.version
208
    gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
209
    gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
210
    gon.max_file_size = current_application_settings.max_attachment_size;
211 212 213 214 215

    if current_user
      gon.current_user_id = current_user.id
      gon.api_token = current_user.private_token
    end
216
  end
217 218

  def check_password_expiration
219
    if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now  && !current_user.ldap_user?
220 221 222
      redirect_to new_profile_password_path and return
    end
  end
223

224
  def ldap_security_check
225
    if current_user && current_user.requires_ldap_check?
226 227 228 229
      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
230 231 232 233
      end
    end
  end

234 235 236 237
  def event_filter
    filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
    @event_filter ||= EventFilter.new(filters)
  end
238

239 240
  def gitlab_ldap_access(&block)
    Gitlab::LDAP::Access.open { |access| block.call(access) }
241 242
  end

243 244 245 246 247 248 249 250 251 252 253 254 255
  # 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
256 257 258 259 260 261 262 263

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

  def configure_permitted_parameters
266
    devise_parameter_sanitizer.sanitize(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
267
  end
268 269 270 271

  def hexdigest(string)
    Digest::SHA1.hexdigest string
  end
272 273 274 275 276 277

  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
278

279
  def set_filters_params
280
    params[:sort] ||= 'created_desc'
281 282 283
    params[:scope] = 'all' if params[:scope].blank?
    params[:state] = 'opened' if params[:state].blank?

284
    @filter_params = params.dup
285 286

    if @project
287
      @filter_params[:project_id] = @project.id
288
    elsif @group
289
      @filter_params[:group_id] = @group.id
290
    else
291 292 293 294 295
      # 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
296
    end
297 298

    @filter_params
299 300 301
  end

  def set_filter_values(collection)
302 303 304
    assignee_id = @filter_params[:assignee_id]
    author_id = @filter_params[:author_id]
    milestone_id = @filter_params[:milestone_id]
305

306
    @sort = @filter_params[:sort]
307 308 309 310 311
    @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?
312
      @assignee = @assignees.find_by(id: assignee_id)
313 314 315
    end

    if author_id.present? && !author_id.to_i.zero?
316
      @author = @authors.find_by(id: author_id)
317 318 319
    end

    if milestone_id.present? && !milestone_id.to_i.zero?
320
      @milestone = @milestones.find_by(id: milestone_id)
321 322
    end
  end
323 324 325 326 327 328 329 330 331 332 333 334 335 336

  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
337 338 339 340 341 342 343 344 345 346 347 348

  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
349
end