jwt_controller.rb 2.73 KB
Newer Older
Kamil Trzcinski's avatar
Kamil Trzcinski committed
1
2
3
class JwtController < ApplicationController
  skip_before_action :authenticate_user!
  skip_before_action :verify_authenticity_token
Kamil Trzcinski's avatar
Kamil Trzcinski committed
4
  before_action :authenticate_project_or_user
Kamil Trzcinski's avatar
Kamil Trzcinski committed
5

6
  SERVICES = {
7
    Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService,
8
9
  }

Kamil Trzcinski's avatar
Kamil Trzcinski committed
10
  def auth
11
12
    service = SERVICES[params[:service]]
    head :not_found unless service
Kamil Trzcinski's avatar
Kamil Trzcinski committed
13

14
    result = service.new(@project, @user, auth_params).execute
15
    return head result[:http_status] if result[:http_status]
Kamil Trzcinski's avatar
Kamil Trzcinski committed
16

17
    render json: result
Kamil Trzcinski's avatar
Kamil Trzcinski committed
18
19
  end

20
  private
Kamil Trzcinski's avatar
Kamil Trzcinski committed
21

Kamil Trzcinski's avatar
Kamil Trzcinski committed
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  def authenticate_project_or_user
    authenticate_with_http_basic do |login, password|
      # if it's possible we first try to authenticate project with login and password
      @project = authenticate_project(login, password)
      return if @project

      @user = authenticate_user(login, password)
      return if @user
    end

    if ActionController::HttpAuthentication::Basic.has_basic_credentials?(request)
      head :forbidden
    end
  end

37
38
  def auth_params
    params.permit(:service, :scope, :offline_token, :account, :client_id)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
39
40
  end

41
  def authenticate_project(login, password)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
42
43
    if login == 'gitlab_ci_token'
      Project.find_by(builds_enabled: true, runners_token: password)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    end
  end

  def authenticate_user(login, password)
    user = Gitlab::Auth.new.find(login, password)

    # If the user authenticated successfully, we reset the auth failure count
    # from Rack::Attack for that IP. A client may attempt to authenticate
    # with a username and blank password first, and only after it receives
    # a 401 error does it present a password. Resetting the count prevents
    # false positives from occurring.
    #
    # Otherwise, we let Rack::Attack know there was a failed authentication
    # attempt from this IP. This information is stored in the Rails cache
    # (Redis) and will be used by the Rack::Attack middleware to decide
    # whether to block requests from this IP.
    config = Gitlab.config.rack_attack.git_basic_auth

    if config.enabled
      if user
        # A successful login will reset the auth failure count from this IP
        Rack::Attack::Allow2Ban.reset(request.ip, config)
      else
        banned = Rack::Attack::Allow2Ban.filter(request.ip, config) do
          # Unless the IP is whitelisted, return true so that Allow2Ban
          # increments the counter (stored in Rails.cache) for the IP
          if config.ip_whitelist.include?(request.ip)
            false
          else
            true
          end
        end

        if banned
          Rails.logger.info "IP #{request.ip} failed to login " \
              "as #{login} but has been temporarily banned from Git auth"
Kamil Trzcinski's avatar
Kamil Trzcinski committed
80
          return
Kamil Trzcinski's avatar
Kamil Trzcinski committed
81
82
83
84
85
86
87
        end
      end
    end

    user
  end
end