Unverified Commit 82b521b4 authored by ThibG's avatar ThibG Committed by GitHub

Merge pull request #724 from ThibG/glitch-soc/merge-upstream

Merge upstream changes
parents 59de2868 f1214f00
This diff is collapsed.
......@@ -10,3 +10,20 @@ libxdamage1
libxfixes3
protobuf-compiler
zlib1g-dev
libcairo2
libcroco3
libdatrie1
libgdk-pixbuf2.0-0
libgraphite2-3
libharfbuzz0b
libpango-1.0-0
libpangocairo-1.0-0
libpangoft2-1.0-0
libpixman-1-0
librsvg2-2
libthai-data
libthai0
libvpx5
libxcb-render0
libxcb-shm0
libxrender1
......@@ -53,10 +53,6 @@ class Api::BaseController < ApplicationController
[params[:limit].to_i.abs, default_limit * 2].min
end
def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end
def current_resource_owner
@current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end
......
......@@ -128,6 +128,10 @@ class ApplicationController < ActionController::Base
protected
def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end
def forbidden
respond_with_error(403)
end
......
......@@ -11,6 +11,7 @@ class Auth::SessionsController < Devise::SessionsController
prepend_before_action :set_pack
before_action :set_instance_presenter, only: [:new]
before_action :set_body_classes
after_action :clear_site_data, only: [:destroy]
def new
Devise.omniauth_configs.each do |provider, config|
......@@ -28,8 +29,10 @@ class Auth::SessionsController < Devise::SessionsController
end
def destroy
tmp_stored_location = stored_location_for(:user)
super
flash.delete(:notice)
store_location_for(:user, tmp_stored_location) if continue_after?
end
protected
......@@ -126,4 +129,16 @@ class Auth::SessionsController < Devise::SessionsController
end
paths
end
def clear_site_data
return if continue_after?
# Should be '"*"' but that doen't work in Chrome (neither does '"executionContexts"')
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
response.headers['Clear-Site-Data'] = '"cache", "cookies"'
end
def continue_after?
truthy_param?(:continue)
end
end
......@@ -18,4 +18,18 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
def set_pack
use_pack 'auth'
end
def render_success
if skip_authorization? || (matching_token? && !truthy_param?('force_login'))
redirect_or_render authorize_response
elsif Doorkeeper.configuration.api_only
render json: pre_auth
else
render :new
end
end
def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end
end
......@@ -8,6 +8,7 @@ module SettingsHelper
bg: 'Български',
ca: 'Català',
co: 'Corsu',
cs: 'Čeština',
cy: 'Cymraeg',
da: 'Dansk',
de: 'Deutsch',
......
......@@ -63,7 +63,7 @@
"column_header.show_settings": "Показать настройки",
"column_header.unpin": "Открепить",
"column_subheading.settings": "Настройки",
"community.column_settings.media_only": "Только медиа",
"community.column_settings.media_only": "Только с медиа",
"compose_form.direct_message_warning": "Этот статус будет виден только упомянутым пользователям.",
"compose_form.direct_message_warning_learn_more": "Узнать больше",
"compose_form.hashtag_warning": "Этот пост не будет показывается в поиске по хэштегу, т.к. он непубличный. Только публичные посты можно найти в поиске по хэштегу.",
......@@ -76,7 +76,7 @@
"compose_form.sensitive.unmarked": "Медиафайлы не отмечены как чувствительные",
"compose_form.spoiler.marked": "Текст скрыт за предупреждением",
"compose_form.spoiler.unmarked": "Текст не скрыт",
"compose_form.spoiler_placeholder": "Напишите свое предупреждение здесь",
"compose_form.spoiler_placeholder": "Текст предупреждения",
"confirmation_modal.cancel": "Отмена",
"confirmations.block.confirm": "Заблокировать",
"confirmations.block.message": "Вы уверены, что хотите заблокировать {name}?",
......@@ -290,7 +290,7 @@
"status.mention": "Упомянуть @{name}",
"status.more": "Больше",
"status.mute": "Заглушить @{name}",
"status.mute_conversation": "Заглушить тред",
"status.mute_conversation": "Заглушить всю цепочку",
"status.open": "Развернуть статус",
"status.pin": "Закрепить в профиле",
"status.pinned": "Закреплённый статус",
......@@ -300,7 +300,7 @@
"status.reblogs.empty": "Никто ещё не продвинул этот статус. Как только кто-то это сделает, они появятся здесь.",
"status.redraft": "Удалить и исправить",
"status.reply": "Ответить",
"status.replyAll": "Ответить на тред",
"status.replyAll": "Ответить всем",
"status.report": "Пожаловаться",
"status.sensitive_toggle": "Нажмите для просмотра",
"status.sensitive_warning": "Чувствительный контент",
......
$black-emojis: '8ball' 'ant' 'back' 'black_circle' 'black_large_square' 'black_medium_small_square' 'black_medium_square' 'black_nib' 'black_small_square' 'bomb' 'bust_in_silhouette' 'camera' 'camera_with_flash' 'clubs' 'copyright' 'curly_loop' 'currency_exchange' 'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'lower_left_fountain_pen' 'on' 'registered' 'soon' 'spades' 'spider' 'tm' 'top' 'waving_black_flag' 'wavy_dash' 'video_game';
$black-emojis: '8ball' 'ant' 'back' 'black_circle' 'black_heart' 'black_large_square' 'black_medium_small_square' 'black_medium_square' 'black_nib' 'black_small_square' 'bomb' 'bowling' 'bust_in_silhouette' 'busts_in_silhouette' 'camera' 'camera_with_flash' 'clubs' 'copyright' 'curly_loop' 'currency_exchange' 'dark_sunglasses' 'eight_pointed_black_star' 'electric_plug' 'end' 'female-guard' 'film_projector' 'fried_egg' 'gorilla' 'guardsman' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'hocho' 'hole' 'joystick' 'kaaba' 'lower_left_ballpoint_pen' 'lower_left_fountain_pen' 'male-guard' 'microphone' 'mortar_board' 'movie_camera' 'musical_score' 'on' 'registered' 'soon' 'spades' 'speaking_head_in_silhouette' 'spider' 'telephone_receiver' 'tm' 'top' 'tophat' 'turkey' 'vhs' 'video_camera' 'video_game' 'water_buffalo' 'waving_black_flag' 'wavy_dash';
%white-emoji-outline {
filter: drop-shadow(1px 1px 0 $white) drop-shadow(-1px 1px 0 $white) drop-shadow(1px -1px 0 $white) drop-shadow(-1px -1px 0 $white);
......
......@@ -1666,14 +1666,14 @@ a.account__display-name {
&.top {
bottom: -5px;
margin-left: -13px;
margin-left: -7px;
border-width: 5px 7px 0;
border-top-color: $ui-secondary-color;
}
&.bottom {
top: -5px;
margin-left: -13px;
margin-left: -7px;
border-width: 0 7px 5px;
border-bottom-color: $ui-secondary-color;
}
......
......@@ -53,6 +53,11 @@ table {
border-spacing: 0;
}
html {
scrollbar-face-color: lighten($ui-base-color, 4%);
scrollbar-track-color: rgba($base-overlay-background, 0.1);
}
::-webkit-scrollbar {
width: 12px;
height: 12px;
......
......@@ -87,34 +87,36 @@ class FetchLinkCardService < BaseService
end
def attempt_oembed
embed = FetchOEmbedService.new.call(@url, html: @html)
service = FetchOEmbedService.new
embed = service.call(@url, html: @html)
url = Addressable::URI.parse(service.endpoint_url)
return false if embed.nil?
@card.type = embed[:type]
@card.title = embed[:title] || ''
@card.author_name = embed[:author_name] || ''
@card.author_url = embed[:author_url] || ''
@card.author_url = embed[:author_url].present? ? (url + embed[:author_url]).to_s : ''
@card.provider_name = embed[:provider_name] || ''
@card.provider_url = embed[:provider_url] || ''
@card.provider_url = embed[:provider_url].present? ? (url + embed[:provider_url]).to_s : ''
@card.width = 0
@card.height = 0
case @card.type
when 'link'
@card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present?
@card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
when 'photo'
return false if embed[:url].blank?
@card.embed_url = embed[:url]
@card.image_remote_url = embed[:url]
@card.embed_url = (url + embed[:url]).to_s
@card.image_remote_url = (url + embed[:url]).to_s
@card.width = embed[:width].presence || 0
@card.height = embed[:height].presence || 0
when 'video'
@card.width = embed[:width].presence || 0
@card.height = embed[:height].presence || 0
@card.html = Formatter.instance.sanitize(embed[:html], Sanitize::Config::MASTODON_OEMBED)
@card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present?
@card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
when 'rich'
# Most providers rely on <script> tags, which is a no-no
return false
......@@ -146,7 +148,7 @@ class FetchLinkCardService < BaseService
@card.title = meta_property(page, 'og:title').presence || page.at_xpath('//title')&.content || ''
@card.description = meta_property(page, 'og:description').presence || meta_property(page, 'description') || ''
@card.image_remote_url = meta_property(page, 'og:image') if meta_property(page, 'og:image')
@card.image_remote_url = (Addressable::URI.parse(@url) + meta_property(page, 'og:image')).to_s if meta_property(page, 'og:image')
return if @card.title.blank? && @card.html.blank?
......
......@@ -31,7 +31,7 @@ class FetchOEmbedService
return if @endpoint_url.blank?
@endpoint_url = Addressable::URI.parse(@endpoint_url).to_s
@endpoint_url = (Addressable::URI.parse(@url) + @endpoint_url).to_s
rescue Addressable::URI::InvalidURIError
@endpoint_url = nil
end
......
......@@ -3,7 +3,10 @@
= image_tag account.header.url, class: 'parallax'
.public-account-header__bar
= link_to short_account_url(account), class: 'avatar' do
= image_tag account.avatar.url
- if current_account&.user&.setting_auto_play_gif
= image_tag account.avatar_original_url
- else
= image_tag account.avatar_static_url
.public-account-header__tabs
.public-account-header__tabs__name
%h1
......
......@@ -5,7 +5,7 @@
.name
= t 'users.signed_in_as'
%span.username @#{current_account.local_username_and_domain}
= link_to destroy_user_session_path, method: :delete, class: 'logout-link icon-button' do
= link_to destroy_user_session_path(continue: true), method: :delete, class: 'logout-link icon-button' do
= fa_icon 'sign-out'
.container-alt= yield
......
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require_relative '../lib/cli'
Mastodon::CLI.start(ARGV)
......@@ -43,6 +43,7 @@ module Mastodon
:bg,
:ca,
:co,
:cs,
:cy,
:da,
:de,
......
......@@ -10,6 +10,7 @@ Warden::Manager.after_set_user except: :fetch do |user, warden|
expires: 1.year.from_now,
httponly: true,
secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'),
same_site: :lax,
}
end
......@@ -20,6 +21,7 @@ Warden::Manager.after_fetch do |user, warden|
expires: 1.year.from_now,
httponly: true,
secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'),
same_site: :lax,
}
else
warden.logout
......
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_mastodon_session', secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true')
Rails.application.config.session_store :cookie_store, key: '_mastodon_session', secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'), same_site: :lax
......@@ -6,7 +6,7 @@ ru:
about_this: Об этом узле
administered_by: 'Администратор узла:'
api: API
apps: Мобильные приложения
apps: Приложения
closed_registrations: В данный момент регистрация на этом узле закрыта. Но вы можете найти другой узел, создать на нём учётную запись и получить доступ к той же сети оттуда.
contact: Связаться
contact_missing: Не установлено
......@@ -51,7 +51,7 @@ ru:
many: подписчиков
one: подписчик
other: подписчиков
following: Подписан(а)
following: подписки
joined: 'Дата регистрации: %{date}'
media: Медиа
moved_html: "%{name} переехал(а) на %{new_profile_link}:"
......@@ -206,7 +206,7 @@ ru:
custom_emojis:
by_domain: Домен
copied_msg: Локальная копия эмодзи успешно создана
copy: Скопироват
copy: Копировать
copy_failed_msg: Не удалось создать локальную копию эмодзи
created_msg: Эмодзи успешно создано!
delete: Удалить
......@@ -231,11 +231,11 @@ ru:
dashboard:
backlog: задачи
config: Конфигурация
feature_deletions: Аккаунтов удалено
feature_deletions: Удаление аккаунтов
feature_invites: Пригласительные ссылки
feature_registrations: Регистрации
feature_relay: Ретрансляторов сети
features: Нововведения
feature_registrations: Регистрация
feature_relay: Ретрансляторы
features: Возможности
hidden_service: Федерация со скрытыми сервисами
open_reports: открытых жалоб
recent_users: Недавние пользователи
......@@ -300,7 +300,7 @@ ru:
search: Поиск
title: Известные узлы
invites:
deactivate_all: Удалить все
deactivate_all: Отключить все
filter:
all: Все
available: Актуальные
......@@ -314,7 +314,7 @@ ru:
inbox_url: URL ретранслятора
pending: Ожидание подтверждения ретранслятора
save_and_enable: Сохранить и включить
setup: Настроте соединение с ретранслятором
setup: Настроить соединение с ретранслятором
status: Состояние
title: Ретрансляторы
report_notes:
......@@ -405,7 +405,7 @@ ru:
title: Расширенное описание узла
site_short_description:
desc_html: Отображается в боковой панели и в тегах. Опишите, что такое Mastodon и что делает именно этот узел особенным. Если пусто, используется описание узла по умолчанию.
title: Короткое описание узла
title: Краткое описание узла
site_terms:
desc_html: Вы можете добавить сюда собственную политику конфиденциальности, пользовательское соглашение и другие документы. Можно использовать теги HTML.
title: Условия использования
......@@ -791,7 +791,7 @@ ru:
ownership: Нельзя закрепить чужой статус
private: Нельзя закрепить непубличный статус
reblog: Нельзя закрепить продвинутый статус
show_more: Подробнее
show_more: Ещё
sign_in_to_participate: Войдите, чтобы принять участие в дискуссии
title: '%{name}: "%{quote}"'
visibilities:
......
......@@ -35,6 +35,8 @@ ru:
data: Файл CSV, экспортированный с другого узла Mastodon
sessions:
otp: 'Введите код двухфакторной аутентификации, сгенерированный в мобильном приложении, или используйте один из Ваших кодов восстановления:'
user:
chosen_languages: Если выбрано, то в публичных лентах будут показаны только посты на выбранных языках
labels:
account:
fields:
......
......@@ -43,6 +43,145 @@ module Mastodon
end
end
option :email, required: true
option :confirmed, type: :boolean
option :role, default: 'user'
option :reattach, type: :boolean
option :force, type: :boolean
desc 'add USERNAME', 'Create a new user'
long_desc <<-LONG_DESC
Create a new user account with a given USERNAME and an
e-mail address provided with --email.
With the --confirmed option, the confirmation e-mail will
be skipped and the account will be active straight away.
With the --role option one of "user", "admin" or "moderator"
can be supplied. Defaults to "user"
With the --reattach option, the new user will be reattached
to a given existing username of an old account. If the old
account is still in use by someone else, you can supply
the --force option to delete the old record and reattach the
username to the new account anyway.
LONG_DESC
def add(username)
account = Account.new(username: username)
password = SecureRandom.hex
user = User.new(email: options[:email], password: password, admin: options[:role] == 'admin', moderator: options[:role] == 'moderator', confirmed_at: Time.now.utc)
if options[:reattach]
account = Account.find_local(username) || Account.new(username: username)
if account.user.present? && !options[:force]
say('The chosen username is currently in use', :red)
say('Use --force to reattach it anyway and delete the other user')
return
elsif account.user.present?
account.user.destroy!
end
end
user.account = account
if user.save
if options[:confirmed]
user.confirmed_at = nil
user.confirm!
end
say('OK', :green)
say("New password: #{password}")
else
user.errors.to_h.each do |key, error|
say('Failure/Error: ', :red)
say(key)
say(' ' + error, :red)
end
end
end
desc 'del USERNAME', 'Delete a user'
long_desc <<-LONG_DESC
Remove a user account with a given USERNAME.
LONG_DESC
def del(username)
account = Account.find_local(username)
if account.nil?
say('No user with such username', :red)
return
end
say("Deleting user with #{account.statuses_count}, this might take a while...")
SuspendAccountService.new.call(account, remove_user: true)
say('OK', :green)
end
option :dry_run, type: :boolean
desc 'cull', 'Remove remote accounts that no longer exist'
long_desc <<-LONG_DESC
Query every single remote account in the database to determine
if it still exists on the origin server, and if it doesn't,
remove it from the database.
Accounts that have had confirmed activity within the last week
are excluded from the checks.
If 10 or more accounts from the same domain cannot be queried
due to a connection error (such as missing DNS records) then
the domain is considered dead, and all other accounts from it
are deleted without further querying.
With the --dry-run option, no deletes will actually be carried
out.
LONG_DESC
def cull
domain_thresholds = Hash.new { |hash, key| hash[key] = 0 }
skip_threshold = 7.days.ago
culled = 0
dead_servers = []
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
Account.remote.where(protocol: :activitypub).partitioned.find_each do |account|
next if account.updated_at >= skip_threshold || account.last_webfingered_at >= skip_threshold
unless dead_servers.include?(account.domain)
begin
code = Request.new(:head, account.uri).perform(&:code)
rescue HTTP::ConnectionError
domain_thresholds[account.domain] += 1
if domain_thresholds[account.domain] >= 10
dead_servers << account.domain
end
rescue StandardError
next
end
end
if [404, 410].include?(code) || dead_servers.include?(account.domain)
unless options[:dry_run]
SuspendAccountService.new.call(account)
account.destroy
end
culled += 1
say('.', :green, false)
else
say('.', nil, false)
end
end
say
say("Removed #{culled} accounts (#{dead_servers.size} dead servers)#{dry_run}", :green)
unless dead_servers.empty?
say('R.I.P.:', :yellow)
dead_servers.each { |domain| say(' ' + domain) }
end
end
private
def rotate_keys_for_account(account, delay = 0)
......
......@@ -4,5 +4,6 @@ dev_null = Logger.new('/dev/null')
Rails.logger = dev_null
ActiveRecord::Base.logger = dev_null
ActiveJob::Base.logger = dev_null
HttpLog.configuration.logger = dev_null
Paperclip.options[:log] = false
# frozen_string_literal: true
namespace :repo do
desc 'Generate the authors.md file'
task :authors do
file = File.open('AUTHORS.md', 'w')
file << <<~HEADER
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
and provided thanks to the work of the following contributors:
HEADER
url = 'https://api.github.com/repos/tootsuite/mastodon/contributors?anon=1'
HttpLog.config.compact_log = true
while url.present?
response = HTTP.get(url)
contributors = Oj.load(response.body)
contributors.each do |c|
file << "* [#{c['login']}](#{c['html_url']})\n" if c['login']
file << "* [#{c['name']}](mailto:#{c['email']})\n" if c['name']
end
url = LinkHeader.parse(response.headers['Link']).find_link(%w(rel next))&.href
end
file << <<~FOOTER
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
FOOTER
end
end
......@@ -30,6 +30,13 @@ RSpec.describe Auth::SessionsController, type: :controller do
expect(response).to redirect_to(new_user_session_path)
end
it 'does not delete redirect location with continue=true' do
sign_in(user, scope: :user)
controller.store_location_for(:user, '/authorize')
delete :destroy, params: { continue: 'true' }
expect(controller.stored_location_for(:user)).to eq '/authorize'
end
end
context 'with a suspended user' do
......
......@@ -5,23 +5,25 @@ require 'rails_helper'
RSpec.describe Oauth::AuthorizationsController, type: :controller do
render_views
let(:app) { Doorkeeper::Application.create!(name: 'test', redirect_uri: 'http://localhost/') }
let(:app) { Doorkeeper::Application.create!(name: 'test', redirect_uri: 'http://localhost/', scopes: 'read') }
describe 'GET #new' do
subject do
get :new, params: { client_id: app.uid, response_type: 'code', redirect_uri: 'http://localhost/' }
get :new, params: { client_id: app.uid, response_type: 'code', redirect_uri: 'http://localhost/', scope: 'read' }
end
shared_examples 'stores location for user' do
it 'stores location for user' do
subject
expect(controller.stored_location_for(:user)).to eq "/oauth/authorize?client_id=#{app.uid}&redirect_uri=http%3A%2F%2Flocalhost%2F&response_type=code"
expect(controller.stored_location_for(:user)).to eq "/oauth/authorize?client_id=#{app.uid}&redirect_uri=http%3A%2F%2Flocalhost%2F&response_type=code&scope=read"
end
end
context 'when signed in' do
let!(:user) { Fabricate(:user) }
before do
sign_in Fabricate(:user), scope: :user
sign_in user, scope: :user
end
it 'returns http success' do
......@@ -35,6 +37,28 @@ RSpec.describe Oauth::AuthorizationsController, type: :controller do
end
include_examples 'stores location for user'
context 'when app is already authorized' do
before do
Doorkeeper::AccessToken.find_or_create_for(
app,
user.id,
app.scopes,
Doorkeeper.configuration.access_token_expires_in,
Doorkeeper.configuration.refresh_token_enabled?
)
end
it 'redirects to callback' do
subject
expect(response).to redirect_to(/\A#{app.redirect_uri}/)
end
it 'does not redirect to callback with force_login=true' do
get :new, params: { client_id: app.uid, response_type: 'code', redirect_uri: 'http://localhost/', scope: 'read', force_login: 'true' }
expect(response.body).to match(/Authorize/)
end
end
end
context 'when not signed in' do
......
......@@ -29,6 +29,7 @@ Devise::Test::ControllerHelpers.module_eval do
value: resource.activate_session(warden.request),
expires: 1.year.from_now,
httponly: true,
same_site: :lax,
}
end
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment