container_registry_authentication_service.rb 3.3 KB
Newer Older
1 2
module Auth
  class ContainerRegistryAuthenticationService < BaseService
3
    include Gitlab::CurrentSettings
4

5 6
    AUDIENCE = 'container_registry'

7 8
    def execute(capabilities: capabilities)
      @capabilities = capabilities
9

10 11
      return error('not found', 404) unless registry.enabled

12
      unless current_user || project
13
        return error('forbidden', 403) unless scope
14 15
      end

K
Kamil Trzcinski 已提交
16
      { token: authorized_token(scope).encoded }
17 18
    end

19 20
    def self.full_access_token(*names)
      registry = Gitlab.config.registry
21
      token = JSONWebToken::RSAToken.new(registry.key)
22 23
      token.issuer = registry.issuer
      token.audience = AUDIENCE
24
      token.expire_time = token_expire_at
25

26
      token[:access] = names.map do |name|
27
        { type: 'repository', name: name, actions: %w(*) }
28
      end
29

30 31 32
      token.encoded
    end

33 34 35 36
    def self.token_expire_at
      Time.now + current_application_settings.container_registry_token_expire_delay.minutes
    end

37 38
    private

K
Kamil Trzcinski 已提交
39 40
    def authorized_token(*accesses)
      token = JSONWebToken::RSAToken.new(registry.key)
41 42 43
      token.issuer = registry.issuer
      token.audience = params[:service]
      token.subject = current_user.try(:username)
44
      token.expire_time = self.class.token_expire_at
45
      token[:access] = accesses.compact
46 47 48
      token
    end

K
Kamil Trzcinski 已提交
49
    def scope
50 51
      return unless params[:scope]

K
Kamil Trzcinski 已提交
52
      @scope ||= process_scope(params[:scope])
53 54 55 56 57
    end

    def process_scope(scope)
      type, name, actions = scope.split(':', 3)
      actions = actions.split(',')
K
Kamil Trzcinski 已提交
58
      return unless type == 'repository'
59

K
Kamil Trzcinski 已提交
60
      process_repository_access(type, name, actions)
61 62 63 64 65 66 67 68 69 70 71 72 73 74
    end

    def process_repository_access(type, name, actions)
      requested_project = Project.find_with_namespace(name)
      return unless requested_project

      actions = actions.select do |action|
        can_access?(requested_project, action)
      end

      { type: type, name: name, actions: actions } if actions.present?
    end

    def can_access?(requested_project, requested_action)
K
Kamil Trzcinski 已提交
75 76
      return false unless requested_project.container_registry_enabled?

77 78
      case requested_action
      when 'pull'
79
        build_can_pull?(requested_project) || user_can_pull?(requested_project)
80
      when 'push'
81
        build_can_push?(requested_project) || user_can_push?(requested_project)
82 83 84 85 86 87 88 89
      else
        false
      end
    end

    def registry
      Gitlab.config.registry
    end
90 91 92

    private

93 94
    def build_can_pull?(requested_project)
      # Build can:
95
      # 1. pull from it's own project (for ex. a build)
96 97 98
      # 2. read images from dependent projects if creator of build is a team member
      @capabilities.include?(:build_read_container_image) &&
        (requested_project == project || can?(current_user, :build_read_container_image, requested_project))
99 100
    end

101 102 103
    def user_can_pull?(requested_project)
      @capabilities.include?(:read_container_image) &&
        can?(current_user, :read_container_image, requested_project)
104 105
    end

106 107 108 109
    def build_can_push?(requested_project)
      # Build can push only to project to from which he originates
      @capabilities.include?(:build_create_container_image) &&
        requested_project == project
110 111
    end

112 113 114
    def user_can_push?(requested_project)
      @capabilities.include?(:create_container_image) &&
        can?(current_user, :create_container_image, requested_project)
115
    end
116 117
  end
end