summaryrefslogtreecommitdiff
path: root/app/services/resource_access_tokens/create_service.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/resource_access_tokens/create_service.rb')
-rw-r--r--app/services/resource_access_tokens/create_service.rb111
1 files changed, 111 insertions, 0 deletions
diff --git a/app/services/resource_access_tokens/create_service.rb b/app/services/resource_access_tokens/create_service.rb
new file mode 100644
index 00000000000..c8e86e68383
--- /dev/null
+++ b/app/services/resource_access_tokens/create_service.rb
@@ -0,0 +1,111 @@
+# 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?
+
+ # We skip authorization by default, since the user creating the bot is not an admin
+ # and project/group bot users are not created via sign-up
+ 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)
+ 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
+ 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
+ }
+ 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