mastodon/app/services/summarize_account_service.rb
Eugen Rochko 7bf27db007 Add option to keep evidence when suspending accounts
Fix #547

When selected, before the account's data is removed, some of it
is denormalized into a separate, symmetrically-encrypted table. In
particular:

- The e-mail
- All IPs used to access the account
- SHA256 fingerprints of all uploaded files
- URIs of accounts followed by or following the account
- URIs of accounts that were invited
2020-01-13 12:06:44 +01:00

142 lines
3.2 KiB
Ruby

# frozen_string_literal: true
class SummarizeAccountService < BaseService
def call(account)
raise ArgumentError, 'Must be a local account' unless account.user&.present?
@account = account
@user = account.user
@sessions = []
@following = []
@followers = []
@invited_by = nil
@invitees = []
@hashes = []
summarize_sessions!
summarize_network!
summarize_media!
SecureAccountSummary.create!(
account_id: @account.id,
summary: Oj.dump(summary_attributes)
)
end
private
def summary_attributes
{
access: {
email: @user.email,
sessions: @sessions.uniq,
},
network: {
following: @following,
followers: @followers,
inviter: @invited_by,
invitees: @invitees,
},
media: {
fingerprints: @hashes.compact.uniq,
},
}
end
def summarize_sessions!
remember_current_session!
remember_last_session!
remember_other_sessions!
end
def summarize_network!
remember_followers!
remember_following!
remember_invitees!
remember_invited_by!
end
def summarize_media!
fingerprint_avatar!
fingerprint_header!
fingerprint_media_attachments!
end
def remember_following!
@account.following.find_each do |account|
@following << account_uri(account)
end
end
def remember_followers!
@account.followers.find_each do |account|
@followers << account_uri(account)
end
end
def remember_invited_by!
@invited_by = account_uri(@user.invite.user.account) if @user.invite&.user&.account&.present?
end
def remember_invitees!
@user.invites.find_each do |invite|
invite.users.find_each do |user|
@invitees << account_uri(user.account)
end
end
end
def remember_current_session!
@sessions << ip_and_timestamp(@user.current_sign_in_ip, @user.current_sign_in_at) if @user.current_sign_in_ip&.present?
end
def remember_last_session!
@sessions << ip_and_timestamp(@user.last_sign_in_ip, @user.last_sign_in_at) if @user.last_sign_in_ip&.present?
end
def remember_other_sessions!
@user.session_activations.find_each do |session_activation|
@sessions << ip_and_timestamp(session_activation.ip, session_activation.updated_at)
end
end
def fingerprint_avatar!
@hashes << fingerprint_attachment(@account.avatar) if @account.avatar.exists?
end
def fingerprint_header!
@hashes << fingerprint_attachment(@account.header) if @account.header.exists?
end
def fingerprint_media_attachments!
@account.media_attachments.find_each do |media_attachment|
@hashes << fingerprint_attachment(media_attachment.file)
end
end
CHUNK_SIZE = 16.kilobytes
def fingerprint_attachment(attachment)
adapter = Paperclip.io_adapters.for(attachment)
digest = Digest::SHA256.new
while (buffer = adapter.read(CHUNK_SIZE))
digest.update(buffer)
end
digest.hexdigest
rescue Errno::ENOENT, Seahorse::Client::NetworkingError
nil
end
def account_uri(account)
ActivityPub::TagManager.instance.uri_for(account)
end
def ip_and_timestamp(ip, timestamp)
[ip&.to_s, timestamp&.iso8601]
end
end