Welcome to mirror list, hosted at ThFree Co, Russian Federation.

create_service.rb « resource_access_tokens « services « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 3aa0e5650eeba019b4fb455a81d6f9a263c4ed4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# frozen_string_literal: true

module ResourceAccessTokens
  class CreateService < BaseService
    def initialize(current_user, resource, params = {})
      @resource_type = resource.class.name.downcase
      @resource = resource
      @current_user = current_user
      @params = params.dup
    end

    def execute
      return unless feature_enabled?
      return error("User does not have permission to create #{resource_type} Access Token") unless has_permission_to_create?

      user = create_user

      return error(user.errors.full_messages.to_sentence) unless user.persisted?
      return error("Failed to provide maintainer access") unless provision_access(resource, user)

      token_response = create_personal_access_token(user)

      if token_response.success?
        success(token_response.payload[:personal_access_token])
      else
        error(token_response.message)
      end
    end

    private

    attr_reader :resource_type, :resource

    def feature_enabled?
      ::Feature.enabled?(:resource_access_token, resource, default_enabled: true)
    end

    def has_permission_to_create?
      case resource_type
      when 'project'
        can?(current_user, :admin_project, resource)
      when 'group'
        can?(current_user, :admin_group, resource)
      else
        false
      end
    end

    def create_user
      # Even project maintainers can create project access tokens, which in turn
      # creates a bot user, and so it becomes necessary to  have `skip_authorization: true`
      # since someone like a project maintainer does not inherently have the ability
      # to create a new user in the system.

      Users::CreateService.new(current_user, default_user_params).execute(skip_authorization: true)
    end

    def default_user_params
      {
        name: params[:name] || "#{resource.name.to_s.humanize} bot",
        email: generate_email,
        username: generate_username,
        user_type: "#{resource_type}_bot".to_sym,
        skip_confirmation: true # Bot users should always have their emails confirmed.
      }
    end

    def generate_username
      base_username = "#{resource_type}_#{resource.id}_bot"

      uniquify.string(base_username) { |s| User.find_by_username(s) }
    end

    def generate_email
      email_pattern = "#{resource_type}#{resource.id}_bot%s@example.com"

      uniquify.string(-> (n) { Kernel.sprintf(email_pattern, n) }) do |s|
        User.find_by_email(s)
      end
    end

    def uniquify
      Uniquify.new
    end

    def create_personal_access_token(user)
      PersonalAccessTokens::CreateService.new(user, personal_access_token_params).execute
    end

    def personal_access_token_params
      {
        name: params[:name] || "#{resource_type}_bot",
        impersonation: false,
        scopes: params[:scopes] || default_scopes,
        expires_at: params[:expires_at] || nil
      }
    end

    def default_scopes
      Gitlab::Auth.resource_bot_scopes
    end

    def provision_access(resource, user)
      resource.add_maintainer(user)
    end

    def error(message)
      ServiceResponse.error(message: message)
    end

    def success(access_token)
      ServiceResponse.success(payload: { access_token: access_token })
    end
  end
end