debian-mirror-gitlab/app/services/resource_access_tokens/create_service.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

135 lines
4.5 KiB
Ruby
Raw Permalink Normal View History

2020-04-22 19:07:51 +05:30
# frozen_string_literal: true
2020-05-24 23:13:21 +05:30
module ResourceAccessTokens
class CreateService < BaseService
2023-05-27 22:25:52 +05:30
include Gitlab::Utils::StrongMemoize
2020-05-24 23:13:21 +05:30
def initialize(current_user, resource, params = {})
@resource_type = resource.class.name.downcase
2020-04-22 19:07:51 +05:30
@resource = resource
2020-05-24 23:13:21 +05:30
@current_user = current_user
2020-04-22 19:07:51 +05:30
@params = params.dup
end
def execute
2021-03-11 19:13:27 +05:30
return error("User does not have permission to create #{resource_type} access token") unless has_permission_to_create?
2020-04-22 19:07:51 +05:30
2022-07-23 23:45:48 +05:30
access_level = params[:access_level] || Gitlab::Access::MAINTAINER
2023-01-10 11:22:00 +05:30
return error("Could not provision owner access to project access token") if do_not_allow_owner_access_level_for_project_bot?(access_level)
2022-07-23 23:45:48 +05:30
2020-04-22 19:07:51 +05:30
user = create_user
return error(user.errors.full_messages.to_sentence) unless user.persisted?
2021-01-29 00:20:46 +05:30
2021-09-04 01:27:46 +05:30
user.update!(external: true) if current_user.external?
2021-09-30 23:02:18 +05:30
member = create_membership(resource, user, access_level)
2021-01-29 00:20:46 +05:30
unless member.persisted?
delete_failed_user(user)
2023-05-27 22:25:52 +05:30
return error("Could not provision #{Gitlab::Access.human_access(access_level.to_i).downcase} access to the access token. ERROR: #{member.errors.full_messages.to_sentence}")
2021-01-29 00:20:46 +05:30
end
2020-04-22 19:07:51 +05:30
token_response = create_personal_access_token(user)
if token_response.success?
2021-03-11 19:13:27 +05:30
log_event(token_response.payload[:personal_access_token])
2020-04-22 19:07:51 +05:30
success(token_response.payload[:personal_access_token])
else
2021-01-29 00:20:46 +05:30
delete_failed_user(user)
2020-04-22 19:07:51 +05:30
error(token_response.message)
end
end
private
2020-05-24 23:13:21 +05:30
attr_reader :resource_type, :resource
2023-05-27 22:25:52 +05:30
def username_and_email_generator
Gitlab::Utils::UsernameAndEmailGenerator.new(
username_prefix: "#{resource_type}_#{resource.id}_bot",
email_domain: "noreply.#{Gitlab.config.gitlab.host}"
)
end
strong_memoize_attr :username_and_email_generator
2020-04-22 19:07:51 +05:30
def has_permission_to_create?
2021-04-29 21:17:54 +05:30
%w(project group).include?(resource_type) && can?(current_user, :create_resource_access_tokens, resource)
2020-04-22 19:07:51 +05:30
end
def create_user
2022-11-25 23:54:43 +05:30
# Even project maintainers/owners can create project access tokens, which in turn
2020-07-28 23:09:34 +05:30
# creates a bot user, and so it becomes necessary to have `skip_authorization: true`
2022-11-25 23:54:43 +05:30
# since someone like a project maintainer/owner does not inherently have the ability
2020-07-28 23:09:34 +05:30
# to create a new user in the system.
2021-09-04 01:27:46 +05:30
::Users::AuthorizedCreateService.new(current_user, default_user_params).execute
2020-04-22 19:07:51 +05:30
end
2021-01-29 00:20:46 +05:30
def delete_failed_user(user)
DeleteUserWorker.perform_async(current_user.id, user.id, hard_delete: true, skip_authorization: true)
end
2020-04-22 19:07:51 +05:30
def default_user_params
{
name: params[:name] || "#{resource.name.to_s.humanize} bot",
2023-05-27 22:25:52 +05:30
email: username_and_email_generator.email,
username: username_and_email_generator.username,
2022-03-02 08:16:31 +05:30
user_type: :project_bot,
2020-07-28 23:09:34 +05:30
skip_confirmation: true # Bot users should always have their emails confirmed.
2020-04-22 19:07:51 +05:30
}
end
def create_personal_access_token(user)
2021-01-29 00:20:46 +05:30
PersonalAccessTokens::CreateService.new(
current_user: user, target_user: user, params: personal_access_token_params
).execute
2020-04-22 19:07:51 +05:30
end
def personal_access_token_params
{
2020-05-24 23:13:21 +05:30
name: params[:name] || "#{resource_type}_bot",
2020-04-22 19:07:51 +05:30
impersonation: false,
scopes: params[:scopes] || default_scopes,
expires_at: params[:expires_at] || nil
}
end
def default_scopes
2020-05-24 23:13:21 +05:30
Gitlab::Auth.resource_bot_scopes
2020-04-22 19:07:51 +05:30
end
2021-09-30 23:02:18 +05:30
def create_membership(resource, user, access_level)
2023-07-09 08:55:56 +05:30
resource.add_member(user, access_level, expires_at: default_pat_expiration)
end
def default_pat_expiration
if Feature.enabled?(:default_pat_expiration)
params[:expires_at].presence || PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
else
params[:expires_at]
end
2020-04-22 19:07:51 +05:30
end
2021-03-11 19:13:27 +05:30
def log_event(token)
::Gitlab::AppLogger.info "PROJECT ACCESS TOKEN CREATION: created_by: #{current_user.username}, project_id: #{resource.id}, token_user: #{token.user.name}, token_id: #{token.id}"
end
2020-04-22 19:07:51 +05:30
def error(message)
ServiceResponse.error(message: message)
end
def success(access_token)
ServiceResponse.success(payload: { access_token: access_token })
end
2023-01-10 11:22:00 +05:30
def do_not_allow_owner_access_level_for_project_bot?(access_level)
resource.is_a?(Project) &&
2023-03-04 22:38:38 +05:30
access_level.to_i == Gitlab::Access::OWNER &&
2023-01-10 11:22:00 +05:30
!current_user.can?(:manage_owners, resource)
end
2020-04-22 19:07:51 +05:30
end
end
2021-03-11 19:13:27 +05:30
2021-06-08 01:23:25 +05:30
ResourceAccessTokens::CreateService.prepend_mod_with('ResourceAccessTokens::CreateService')