Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master

remotes/1727458204337373841/tmp_refs/heads/signup-info-prompt
Jenkins 2018-03-21 17:17:20 +00:00
commit 6290cd7969
14 changed files with 105 additions and 57 deletions

View File

@ -4,7 +4,6 @@
[ [
"env", "env",
{ {
"debug": true,
"exclude": ["transform-async-to-generator", "transform-regenerator"], "exclude": ["transform-async-to-generator", "transform-regenerator"],
"loose": true, "loose": true,
"modules": false, "modules": false,

View File

@ -73,7 +73,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 0.10' gem 'rqrcode', '~> 0.10'
gem 'ruby-oembed', '~> 0.12', require: 'oembed' gem 'ruby-oembed', '~> 0.12', require: 'oembed'
gem 'ruby-progressbar', '~> 1.4' gem 'ruby-progressbar', '~> 1.4'
gem 'sanitize', '~> 4.4' gem 'sanitize', '~> 4.6.4'
gem 'sidekiq', '~> 5.0' gem 'sidekiq', '~> 5.0'
gem 'sidekiq-scheduler', '~> 2.1' gem 'sidekiq-scheduler', '~> 2.1'
gem 'sidekiq-unique-jobs', '~> 5.0' gem 'sidekiq-unique-jobs', '~> 5.0'

View File

@ -290,7 +290,7 @@ GEM
activesupport (>= 4, < 5.2) activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2) railties (>= 4, < 5.2)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.1.1) loofah (2.2.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.0)
@ -318,9 +318,9 @@ GEM
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (4.2.0) net-ssh (4.2.0)
nio4r (2.1.0) nio4r (2.1.0)
nokogiri (1.8.1) nokogiri (1.8.2)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
nokogumbo (1.4.13) nokogumbo (1.5.0)
nokogiri nokogiri
nsa (0.2.4) nsa (0.2.4)
activesupport (>= 4.2, < 6) activesupport (>= 4.2, < 6)
@ -499,10 +499,10 @@ GEM
rufus-scheduler (3.4.2) rufus-scheduler (3.4.2)
et-orbi (~> 1.0) et-orbi (~> 1.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (4.5.0) sanitize (4.6.4)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1) nokogumbo (~> 1.4)
sass (3.5.3) sass (3.5.3)
sass-listen (~> 4.0.0) sass-listen (~> 4.0.0)
sass-listen (4.0.0) sass-listen (4.0.0)
@ -704,7 +704,7 @@ DEPENDENCIES
rubocop rubocop
ruby-oembed (~> 0.12) ruby-oembed (~> 0.12)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
sanitize (~> 4.4) sanitize (~> 4.6.4)
scss_lint (~> 0.55) scss_lint (~> 0.55)
sidekiq (~> 5.0) sidekiq (~> 5.0)
sidekiq-bulk (~> 0.1.1) sidekiq-bulk (~> 0.1.1)

View File

@ -1,4 +1,5 @@
import api from '../api'; import api from '../api';
import { CancelToken } from 'axios';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light';
import { tagHistory } from '../settings'; import { tagHistory } from '../settings';
@ -11,6 +12,8 @@ import {
refreshPublicTimeline, refreshPublicTimeline,
} from './timelines'; } from './timelines';
let cancelFetchComposeSuggestionsAccounts;
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
@ -257,13 +260,22 @@ export function undoUploadCompose(media_id) {
}; };
export function clearComposeSuggestions() { export function clearComposeSuggestions() {
if (cancelFetchComposeSuggestionsAccounts) {
cancelFetchComposeSuggestionsAccounts();
}
return { return {
type: COMPOSE_SUGGESTIONS_CLEAR, type: COMPOSE_SUGGESTIONS_CLEAR,
}; };
}; };
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => { const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
if (cancelFetchComposeSuggestionsAccounts) {
cancelFetchComposeSuggestionsAccounts();
}
api(getState).get('/api/v1/accounts/search', { api(getState).get('/api/v1/accounts/search', {
cancelToken: new CancelToken(cancel => {
cancelFetchComposeSuggestionsAccounts = cancel;
}),
params: { params: {
q: token.slice(1), q: token.slice(1),
resolve: false, resolve: false,

View File

@ -94,9 +94,16 @@ class Request
class Socket < TCPSocket class Socket < TCPSocket
class << self class << self
def open(host, *args) def open(host, *args)
address = IPSocket.getaddress(host) outer_e = nil
raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address) Addrinfo.foreach(host, nil, nil, :SOCK_STREAM) do |address|
super address, *args begin
raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address.ip_address)
return super address.ip_address, *args
rescue => e
outer_e = e
end
end
raise outer_e if outer_e
end end
alias new open alias new open

View File

@ -47,7 +47,8 @@
# #
class Account < ApplicationRecord class Account < ApplicationRecord
MENTION_RE = /(?<=^|[^\/[:word:]])@(([a-z0-9_]+)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.]+[a-z0-9_]+)?/i
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE}?)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
include AccountAvatar include AccountAvatar
include AccountFinderConcern include AccountFinderConcern
@ -70,7 +71,8 @@ class Account < ApplicationRecord
validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? } validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? }
# Local user validations # Local user validations
validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? } validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? }
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? } validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? } validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
validate :note_length_does_not_exceed_length_limit, if: -> { local? && will_save_change_to_note? } validate :note_length_does_not_exceed_length_limit, if: -> { local? && will_save_change_to_note? }

View File

@ -16,7 +16,7 @@ class ActivityPub::ProcessAccountService < BaseService
RedisLock.acquire(lock_options) do |lock| RedisLock.acquire(lock_options) do |lock|
if lock.acquired? if lock.acquired?
@account = Account.find_by(uri: @uri) @account = Account.find_remote(@username, @domain)
@old_public_key = @account&.public_key @old_public_key = @account&.public_key
@old_protocol = @account&.protocol @old_protocol = @account&.protocol

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class UniqueUsernameValidator < ActiveModel::Validator
def validate(account)
return if account.username.nil?
normalized_username = account.username.downcase.delete('.')
scope = Account.where(domain: nil, username: normalized_username)
scope = scope.where.not(id: account.id) if account.persisted?
account.errors.add(:username, :taken) if scope.exists?
end
end

View File

@ -77,9 +77,7 @@ module Mastodon
] ]
config.i18n.default_locale = ENV['DEFAULT_LOCALE']&.to_sym config.i18n.default_locale = ENV['DEFAULT_LOCALE']&.to_sym
if config.i18n.available_locales.include?(config.i18n.default_locale) unless config.i18n.available_locales.include?(config.i18n.default_locale)
config.i18n.fallbacks = [:en]
else
config.i18n.default_locale = :en config.i18n.default_locale = :en
end end

View File

@ -55,8 +55,8 @@ Rails.application.configure do
# config.action_mailer.raise_delivery_errors = false # config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found). # English when a translation cannot be found).
config.i18n.fallbacks = true config.i18n.fallbacks = [:en]
# Send deprecation notices to registered listeners. # Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify config.active_support.deprecation = :notify

View File

@ -55,6 +55,8 @@ module Devise
@@ldap_bind_dn = nil @@ldap_bind_dn = nil
mattr_accessor :ldap_password mattr_accessor :ldap_password
@@ldap_password = nil @@ldap_password = nil
mattr_accessor :ldap_tls_no_verify
@@ldap_tls_no_verify = false
class Strategies::PamAuthenticatable class Strategies::PamAuthenticatable
def valid? def valid?
@ -357,5 +359,6 @@ Devise.setup do |config|
config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN') config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN')
config.ldap_password = ENV.fetch('LDAP_PASSWORD') config.ldap_password = ENV.fetch('LDAP_PASSWORD')
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn') config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
end end
end end

View File

@ -1,49 +1,53 @@
# frozen_string_literal: true # frozen_string_literal: true
if ENV['LDAP_ENABLED'] == 'true' require 'net/ldap'
require 'net/ldap' require 'devise/strategies/authenticatable'
require 'devise/strategies/authenticatable'
module Devise module Devise
module Strategies module Strategies
class LdapAuthenticatable < Authenticatable class LdapAuthenticatable < Authenticatable
def authenticate! def authenticate!
if params[:user] if params[:user]
ldap = Net::LDAP.new( ldap = Net::LDAP.new(
host: Devise.ldap_host, host: Devise.ldap_host,
port: Devise.ldap_port, port: Devise.ldap_port,
base: Devise.ldap_base, base: Devise.ldap_base,
encryption: { encryption: {
method: Devise.ldap_method, method: Devise.ldap_method,
tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, tls_options: tls_options,
}, },
auth: { auth: {
method: :simple, method: :simple,
username: Devise.ldap_bind_dn, username: Devise.ldap_bind_dn,
password: Devise.ldap_password, password: Devise.ldap_password,
}, },
connect_timeout: 10 connect_timeout: 10
) )
if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password)) if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password))
user = User.ldap_get_user(user_info.first) user = User.ldap_get_user(user_info.first)
success!(user) success!(user)
else else
return fail(:invalid_login) return fail(:invalid_login)
end
end end
end end
end
def email def email
params[:user][:email] params[:user][:email]
end end
def password def password
params[:user][:password] params[:user][:password]
end
def tls_options
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.tap do |options|
options[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if Devise.ldap_tls_no_verify
end end
end end
end end
end end
Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)
end end
Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)

View File

@ -21,7 +21,7 @@ module Mastodon
end end
def flags def flags
'rc3' 'rc4'
end end
def to_a def to_a

View File

@ -48,6 +48,13 @@ describe Request do
expect(a_request(:get, 'http://example.com')).to have_been_made.once expect(a_request(:get, 'http://example.com')).to have_been_made.once
end end
it 'executes a HTTP request when the first address is private' do
allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM)
.and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM))
.and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:4860:4860::8844"], :PF_INET6, :SOCK_STREAM))
expect(a_request(:get, 'http://example.com')).to have_been_made.once
end
it 'sets headers' do it 'sets headers' do
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
end end
@ -61,7 +68,9 @@ describe Request do
end end
it 'raises Mastodon::ValidationError' do it 'raises Mastodon::ValidationError' do
allow(IPSocket).to receive(:getaddress).with('example.com').and_return('0.0.0.0') allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM)
.and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM))
.and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:db8::face"], :PF_INET6, :SOCK_STREAM))
expect{ subject.perform }.to raise_error Mastodon::ValidationError expect{ subject.perform }.to raise_error Mastodon::ValidationError
end end
end end