Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ group :test do

# Stub web requests for specs
gem 'webmock', '~> 3.18'

# Websocket driver for testing integration between rails/sidekiq and streaming
gem 'websocket-driver', '~> 0.8', require: false
end

group :development do
Expand Down
7 changes: 4 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ GEM
tzinfo
validate_url
webfinger (~> 2.0)
openssl (3.3.0)
openssl (3.3.1)
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
opentelemetry-api (1.6.0)
Expand Down Expand Up @@ -649,7 +649,7 @@ GEM
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.8.1)
rack (3.1.16)
rack (3.2.3)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-cors (3.0.0)
Expand Down Expand Up @@ -906,7 +906,7 @@ GEM
unicode-display_width (3.1.5)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
uri (1.0.4)
useragent (0.16.11)
validate_url (1.0.15)
activemodel (>= 3.0.0)
Expand Down Expand Up @@ -1103,6 +1103,7 @@ DEPENDENCIES
webauthn (~> 3.0)
webmock (~> 3.18)
webpush!
websocket-driver (~> 0.8)
xorcist (~> 1.1)

RUBY VERSION
Expand Down
8 changes: 7 additions & 1 deletion app/controllers/admin/dashboard_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ def index

@pending_appeals_count = Appeal.pending.async_count
@pending_reports_count = Report.unresolved.async_count
@pending_tags_count = Tag.pending_review.async_count
@pending_tags_count = pending_tags.async_count
@pending_users_count = User.pending.async_count
@system_checks = Admin::SystemCheck.perform(current_user)
@time_period = (29.days.ago.to_date...Time.now.utc.to_date)
end

private

def pending_tags
::Trends::TagFilter.new(status: :pending_review).results
end
end
end
9 changes: 2 additions & 7 deletions app/javascript/mastodon/components/featured_carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useDrag } from '@use-gesture/react';
import { expandAccountFeaturedTimeline } from '@/mastodon/actions/timelines';
import { Icon } from '@/mastodon/components/icon';
import { IconButton } from '@/mastodon/components/icon_button';
import StatusContainer from '@/mastodon/containers/status_container';
import { StatusQuoteManager } from '@/mastodon/components/status_quoted';
import { usePrevious } from '@/mastodon/hooks/usePrevious';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
Expand Down Expand Up @@ -218,12 +218,7 @@ const FeaturedCarouselItem: React.FC<
ref={handleRef}
{...props}
>
<StatusContainer
// @ts-expect-error inferred props are wrong
id={statusId}
contextType='account'
withCounters
/>
<StatusQuoteManager id={statusId} contextType='account' withCounters />
</animated.div>
);
};
42 changes: 42 additions & 0 deletions app/javascript/styles/entrypoints/mailer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ table + p {
padding: 24px;
}

.email-inner-nested-card-td {
border-radius: 12px;
padding: 18px;
overflow: hidden;
background-color: #fff;
border: 1px solid #dfdee3;
}

// Account
.email-account-banner-table {
background-color: #f3f2f5;
Expand Down Expand Up @@ -559,12 +567,29 @@ table + p {
}
}

.email-quote-header-img {
width: 34px;

img {
width: 34px;
height: 34px;
border-radius: 8px;
overflow: hidden;
}
}

.email-status-header-text {
padding-left: 16px;
padding-right: 16px;
vertical-align: middle;
}

.email-quote-header-text {
padding-left: 14px;
padding-right: 14px;
vertical-align: middle;
}

.email-status-header-name {
font-size: 16px;
font-weight: 600;
Expand All @@ -578,6 +603,19 @@ table + p {
color: #746a89;
}

.email-quote-header-name {
font-size: 14px;
font-weight: 600;
line-height: 18px;
color: #17063b;
}

.email-quote-header-handle {
font-size: 13px;
line-height: 18px;
color: #746a89;
}

.email-status-content {
padding-top: 24px;
}
Expand All @@ -589,6 +627,10 @@ table + p {
}

.email-status-prose {
.quote-inline {
display: none;
}

p {
font-size: 14px;
line-height: 20px;
Expand Down
2 changes: 1 addition & 1 deletion app/lib/activitypub/parser/status_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def quote_approval_uri
def quote_subpolicy(subpolicy)
flags = 0

allowed_actors = as_array(subpolicy)
allowed_actors = as_array(subpolicy).dup
allowed_actors.uniq!

flags |= Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] if allowed_actors.delete('as:Public') || allowed_actors.delete('Public') || allowed_actors.delete('https://www.w3.org/ns/activitystreams#Public')
Expand Down
2 changes: 1 addition & 1 deletion app/lib/permalink_redirector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def object
end

def redirect_path
return ActivityPub::TagManager.instance.url_for(object) if object.present?
return ActivityPub::TagManager.instance.url_for(object) || ActivityPub::TagManager.instance.uri_for(object) if object.present?

@path.delete_prefix('/deck') if @path.start_with?('/deck')
end
Expand Down
4 changes: 4 additions & 0 deletions app/models/concerns/account/suspensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def suspend!(date: Time.now.utc, origin: :local, block_email: true)
update!(suspended_at: date, suspension_origin: origin)
create_canonical_email_block! if block_email
end

# This terminates all connections for the given account with the streaming
# server:
redis.publish("timeline:system:#{id}", Oj.dump(event: :kill)) if local?
end

def unsuspend!
Expand Down
17 changes: 13 additions & 4 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ def valid_invitation?

def disable!
update!(disabled: true)

# This terminates all connections for the given account with the streaming
# server:
redis.publish("timeline:system:#{account.id}", Oj.dump(event: :kill))
end

def enable!
Expand Down Expand Up @@ -378,17 +382,22 @@ def revoke_access!
end

def reset_password!
# First, change password to something random, this revokes sessions and on-going access:
change_password!(SecureRandom.hex)

# Finally, send a reset password prompt to the user
send_reset_password_instructions
end

def change_password!(new_password)
# First, change password to something random and deactivate all sessions
transaction do
update(password: SecureRandom.hex)
update(password: new_password)
session_activations.destroy_all
end

# Then, remove all authorized applications and connected push subscriptions
revoke_access!

# Finally, send a reset password prompt to the user
send_reset_password_instructions
end

protected
Expand Down
2 changes: 2 additions & 0 deletions app/services/activitypub/process_status_update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ def handle_implicit_update!
update_quote_approval!
update_counts!
end

broadcast_updates! if @status.quote&.state_previously_changed?
end

def update_interaction_policies!
Expand Down
17 changes: 17 additions & 0 deletions app/views/notification_mailer/_nested_quote.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.email-quote-header-img
= image_tag full_asset_url(status.account.avatar.url), alt: '', width: 34, height: 34
%td.email-quote-header-text
%h2.email-quote-header-name
= display_name(status.account)
%p.email-quote-header-handle
@#{status.account.pretty_acct}

%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.email-status-content
= render 'status_content', status: status

%p.email-status-footer
= link_to l(status.created_at.in_time_zone(time_zone.presence), format: :with_time_zone), web_url("@#{status.account.pretty_acct}/#{status.id}")
21 changes: 6 additions & 15 deletions app/views/notification_mailer/_status.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,12 @@
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.email-status-content
.auto-dir
- if status.spoiler_text?
%p.email-status-spoiler
= status.spoiler_text

.email-status-prose
= status_content_format(status)

- if status.ordered_media_attachments.size.positive?
%p.email-status-media
- status.ordered_media_attachments.each do |a|
- if status.local?
= link_to full_asset_url(a.file.url(:original)), full_asset_url(a.file.url(:original))
- else
= link_to a.remote_url, a.remote_url
= render 'status_content', status: status

- if status.local? && status.quote
%table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
%td.email-inner-nested-card-td
= render 'nested_quote', status: status.quote.quoted_status, time_zone: time_zone
%p.email-status-footer
= link_to l(status.created_at.in_time_zone(time_zone.presence), format: :with_time_zone), web_url("@#{status.account.pretty_acct}/#{status.id}")
4 changes: 4 additions & 0 deletions app/views/notification_mailer/_status.text.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
>
<% end %>
> <%= raw word_wrap(extract_status_plain_text(status), break_sequence: "\n> ") %>
<% if status.local? && status.quote %>
>
>> <%= raw word_wrap(extract_status_plain_text(status.quote.quoted_status), break_sequence: "\n>> ") %>
<% end %>

<%= raw t('application_mailer.view')%> <%= web_url("@#{status.account.pretty_acct}/#{status.id}") %>
15 changes: 15 additions & 0 deletions app/views/notification_mailer/_status_content.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.auto-dir
- if status.spoiler_text?
%p.email-status-spoiler
= status.spoiler_text

.email-status-prose
= status_content_format(status)

- if status.ordered_media_attachments.size.positive?
%p.email-status-media
- status.ordered_media_attachments.each do |a|
- if status.local?
= link_to full_asset_url(a.file.url(:original)), full_asset_url(a.file.url(:original))
- else
= link_to a.remote_url, a.remote_url
1 change: 1 addition & 0 deletions app/workers/activitypub/refetch_and_verify_quote_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class ActivityPub::RefetchAndVerifyQuoteWorker
def perform(quote_id, quoted_uri, options = {})
quote = Quote.find(quote_id)
ActivityPub::VerifyQuoteService.new.call(quote, fetchable_quoted_uri: quoted_uri, request_id: options[:request_id])
::DistributionWorker.perform_async(quote.status_id, { 'update' => true }) if quote.state_previously_changed?
rescue ActiveRecord::RecordNotFound
# Do nothing
true
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ services:
web:
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
build: .
image: kmyblue:20.2-dev
image: kmyblue:20.3
restart: always
env_file: .env.production
command: bundle exec puma -C config/puma.rb
Expand All @@ -83,7 +83,7 @@ services:
build:
dockerfile: ./streaming/Dockerfile
context: .
image: kmyblue-streaming:20.2-dev
image: kmyblue-streaming:20.3
restart: always
env_file: .env.production
command: node ./streaming/index.js
Expand All @@ -101,7 +101,7 @@ services:

sidekiq:
build: .
image: kmyblue:20.2-dev
image: kmyblue:20.3
restart: always
env_file: .env.production
command: bundle exec sidekiq
Expand Down
7 changes: 5 additions & 2 deletions lib/mastodon/cli/accounts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,17 @@ def modify(username)
user.role_id = nil
end

password = SecureRandom.hex if options[:reset_password]
user.password = password if options[:reset_password]
user.email = options[:email] if options[:email]
user.disabled = false if options[:enable]
user.disabled = true if options[:disable]
user.approved = true if options[:approve]
user.disable_two_factor! if options[:disable_2fa]

# Password changes are a little different, as we also need to ensure
# sessions, subscriptions, and access tokens are revoked after changing:
password = SecureRandom.hex if options[:reset_password]
user.change_password!(password) if options[:reset_password]

if user.save
user.confirm if options[:confirm]

Expand Down
2 changes: 1 addition & 1 deletion lib/mastodon/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def kmyblue_major
end

def kmyblue_minor
2
3
end

def kmyblue_flag
Expand Down
13 changes: 11 additions & 2 deletions spec/lib/mastodon/cli/accounts_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,20 @@ def account_from_options
context 'with --reset-password option' do
let(:options) { { reset_password: true } }

let(:user) { Fabricate(:user, password: original_password) }
let(:original_password) { 'foobar12345' }
let(:new_password) { 'new_password12345' }

it 'returns a new password for the user' do
allow(SecureRandom).to receive(:hex).and_return('new_password')
allow(SecureRandom).to receive(:hex).and_return(new_password)
allow(Account).to receive(:find_local).and_return(user.account)
allow(user).to receive(:change_password!).and_call_original

expect { subject }
.to output_results('new_password')
.to output_results(new_password)

expect(user).to have_received(:change_password!).with(new_password)
expect(user.reload).to_not be_external_or_valid_password(original_password)
end
end

Expand Down
Loading