Merge branch 'next-minor' into develop

This commit is contained in:
Benjamin Neff 2019-05-12 00:25:52 +02:00
commit 314239ff2a
No known key found for this signature in database
GPG key ID: 971464C3F1A90194
27 changed files with 197 additions and 96 deletions

1
.gitignore vendored
View file

@ -11,7 +11,6 @@ app/assets/images/custom/
# Configuration files # Configuration files
config/diaspora.yml config/diaspora.yml
config/initializers/secret_token.rb config/initializers/secret_token.rb
config/initializers/twofa_encryption_key.rb
.bundle .bundle
vendor/bundle/ vendor/bundle/
vendor/cache/ vendor/cache/

View file

@ -20,6 +20,8 @@
## Bug fixes ## Bug fixes
## Features ## Features
* Add line mentioning diaspora\* on the splash page [#7966](https://github.com/diaspora/diaspora/pull/7966)
* Improve communication about signing up on closed pods [#7896](https://github.com/diaspora/diaspora/pull/7896)
# 0.7.11.0 # 0.7.11.0
@ -27,6 +29,9 @@
* Enable paranoid mode for devise [#8003](https://github.com/diaspora/diaspora/pull/8003) * Enable paranoid mode for devise [#8003](https://github.com/diaspora/diaspora/pull/8003)
* Refactor likes cucumber test [#8002](https://github.com/diaspora/diaspora/pull/8002) * Refactor likes cucumber test [#8002](https://github.com/diaspora/diaspora/pull/8002)
## Bug fixes
* Fix old photos without remote url for export [#8012](https://github.com/diaspora/diaspora/pull/8012)
## Features ## Features
* Add a manifest.json file as a first step to make diaspora\* a Progressive Web App [#7998](https://github.com/diaspora/diaspora/pull/7998) * Add a manifest.json file as a first step to make diaspora\* a Progressive Web App [#7998](https://github.com/diaspora/diaspora/pull/7998)
* Allow `web+diaspora://` links to link to a profile with only the diaspora ID [#8000](https://github.com/diaspora/diaspora/pull/8000) * Allow `web+diaspora://` links to link to a profile with only the diaspora ID [#8000](https://github.com/diaspora/diaspora/pull/8000)

View file

@ -42,6 +42,14 @@
padding: 15px; padding: 15px;
} }
.part-of-diaspora {
font-style: italic;
a {
color: $white;
}
}
.login-form { .login-form {
fieldset { background: none; } fieldset { background: none; }

View file

@ -1,5 +1,4 @@
.page-registrations.action-new, .page-registrations {
.page-registrations.action-create {
.ball { .ball {
background: image-url('branding/ball.png') no-repeat; background: image-url('branding/ball.png') no-repeat;
background-size: contain; background-size: contain;
@ -12,19 +11,24 @@
height: 633px; height: 633px;
} }
@media (max-width: $screen-xs-max) {
.v-center {
height: auto;
}
}
.content { .content {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
h2 { h1 {
font-size: 35px; font-size: 35px;
margin: 12px; margin: 12px 0;
text-align: center;
} }
} }
form { form {
max-width: 400px; max-width: 500px;
} }
.captcha-img { .captcha-img {

View file

@ -5,9 +5,9 @@
# the COPYRIGHT file. # the COPYRIGHT file.
class RegistrationsController < Devise::RegistrationsController class RegistrationsController < Devise::RegistrationsController
before_action :check_registrations_open_or_valid_invite! before_action :check_registrations_open_or_valid_invite!, except: :registrations_closed
layout -> { request.format == :mobile ? "application" : "with_header" } layout -> { request.format == :mobile ? "application" : "with_header_with_footer" }
def create def create
@user = User.build(user_params) @user = User.build(user_params)
@ -28,13 +28,17 @@ class RegistrationsController < Devise::RegistrationsController
end end
end end
def registrations_closed
render "registrations/registrations_closed"
end
private private
def check_registrations_open_or_valid_invite! def check_registrations_open_or_valid_invite!
return true if AppConfig.settings.enable_registrations? || invite.try(:can_be_used?) return true if AppConfig.settings.enable_registrations? || invite.try(:can_be_used?)
flash[:error] = params[:invite] ? t("registrations.invalid_invite") : t("registrations.closed") flash[:error] = t("registrations.invalid_invite") if params[:invite]
redirect_to new_user_session_path redirect_to registrations_closed_path
end end
def invite def invite

View file

@ -19,11 +19,10 @@ class User < ApplicationRecord
scope :halfyear_actives, ->(time = Time.now) { logged_in_since(time - 6.month) } scope :halfyear_actives, ->(time = Time.now) { logged_in_since(time - 6.month) }
scope :active, -> { joins(:person).where(people: {closed_account: false}) } scope :active, -> { joins(:person).where(people: {closed_account: false}) }
attribute :otp_secret attr_encrypted :otp_secret, if: false, prefix: "plain_"
devise :two_factor_authenticatable, devise :two_factor_authenticatable,
:two_factor_backupable, :two_factor_backupable,
otp_secret_encryption_key: AppConfig.twofa_encryption_key,
otp_backup_code_length: 16, otp_backup_code_length: 16,
otp_number_of_backup_codes: 10 otp_number_of_backup_codes: 10

View file

@ -4,6 +4,10 @@
.row .row
.col-md-8.text-center .col-md-8.text-center
%h1= t("home.default.headline", pod_name: pod_name) %h1= t("home.default.headline", pod_name: pod_name)
- if pod_name != "diaspora*"
%h2.part-of-diaspora
!= t("home.default.part_of_diaspora",
diaspora_site_link: link_to(t("home.default.diaspora_site_link"), "https://diasporafoundation.org/"))
%h2= t("home.default.byline") %h2= t("home.default.byline")
.col-md-4.login-form .col-md-4.login-form
= render partial: "sessions/form", locals: {mobile: false, resource: User.new, resource_name: :user} = render partial: "sessions/form", locals: {mobile: false, resource: User.new, resource_name: :user}

View file

@ -1,5 +1,5 @@
%ul.nav.navbar-nav.navbar-right %ul.nav.navbar-nav.navbar-right
- if AppConfig.settings.enable_registrations? && !current_page?(controller: "/registrations", action: :new) - unless current_page?(controller: "/registrations", action: :new)
%li= link_to t("devise.shared.links.sign_up"), new_user_registration_path, class: "login" %li= link_to t("devise.shared.links.sign_up"), new_user_registration_path, class: "login"
- unless current_page?(controller: "/sessions", action: :new) - unless current_page?(controller: "/sessions", action: :new)
%li= link_to t("devise.shared.links.sign_in"), new_user_session_path, class: "login" %li= link_to t("devise.shared.links.sign_in"), new_user_session_path, class: "login"

View file

@ -4,7 +4,7 @@
- if mobile - if mobile
%legend %legend
= image_tag("branding/logos/header-logo2x.png", height: 40, width: 40) = image_tag("branding/logos/header-logo2x.png", height: 40, width: 40)
= t("aspects.aspect_stream.make_something") = AppConfig.settings.pod_name
- if mobile - if mobile
= f.label :email, t("registrations.new.email"), class: "control-label", id: "emailLabel" = f.label :email, t("registrations.new.email"), class: "control-label", id: "emailLabel"

View file

@ -0,0 +1,11 @@
%h2
= t("devise.shared.links.sign_up_closed")
!= t("registrations.closed.closed_pod",
wiki: link_to(t("registrations.closed.another_pod"), "https://diasporafoundation.org/getting_started/sign_up"))
!= t("registrations.closed.find_pods",
poduptime: link_to("Poduptime", "https://podupti.me/"))
!= t("registrations.closed.other_questions",
wiki: link_to("Wiki", "https://wiki.diasporafoundation.org/Choosing_a_pod"))

View file

@ -1,12 +1,11 @@
#registration #registration
.container .container
.row .row
.col-md-10.offset1 .col-sm-6.hidden-xs
.col-md-7.hidden-phone .ball
%h1.ball .col-sm-6.col-xs-12.v-center
.col-md-5.v-center .content.text-center
.content.text-center %h1#pod-name
%h2#pod-name = AppConfig.settings.pod_name
= AppConfig.settings.pod_name
= render partial: "form", locals: {mobile: false} = render partial: "form", locals: {mobile: false}

View file

@ -0,0 +1,8 @@
#registration
.container
.row
.col-sm-6.hidden-xs
.ball
.col-sm-6.col-xs-12.v-center
.content
= render partial: "registrations_closed"

View file

@ -0,0 +1,10 @@
.stream#main-stream
- flash.each do |name, msg|
.expose#flash-container
.flash-message{class: "message alert alert-#{flash_class name}", role: "alert"}
= msg
.login-form
.login-container
= render partial: "registrations_closed"

View file

@ -553,6 +553,8 @@ en:
home: home:
default: default:
headline: "Welcome to %{pod_name}" headline: "Welcome to %{pod_name}"
part_of_diaspora: "Part of the %{diaspora_site_link}"
diaspora_site_link: "diaspora* federated network"
byline: "The online social world where you are in control" byline: "The online social world where you are in control"
be_who_you_want_to_be: "Be who you want to be" be_who_you_want_to_be: "Be who you want to be"
be_who_you_want_to_be_info: "A lot of networks insist that you use your real identity. Not diaspora*. Here you can choose who you want to be, and share as much or as little about yourself as you want. It really is up to you how you want to interact with other people." be_who_you_want_to_be_info: "A lot of networks insist that you use your real identity. Not diaspora*. Here you can choose who you want to be, and share as much or as little about yourself as you want. It really is up to you how you want to interact with other people."
@ -1050,7 +1052,11 @@ en:
terms_link: "terms of service" terms_link: "terms of service"
create: create:
success: "Youve joined diaspora*!" success: "Youve joined diaspora*!"
closed: "Signups are closed on this diaspora* pod." closed:
closed_pod: "This pod is currently closed to new registrations. However, you can still join the diaspora* network by registering on %{wiki}. Because all pods are interconnected, you will have access to the same content there."
another_pod: "another pod"
find_pods: "Theres a list of pods you can sign up to at %{poduptime}."
other_questions: "If you have any other questions regarding choosing a pod, check out our %{wiki}."
invalid_invite: "The invite link you provided is no longer valid!" invalid_invite: "The invite link you provided is no longer valid!"
reshares: reshares:

View file

@ -129,6 +129,7 @@ Rails.application.routes.draw do
devise_scope :user do devise_scope :user do
get "/users/sign_up" => "registrations#new", :as => :new_user_registration get "/users/sign_up" => "registrations#new", :as => :new_user_registration
post "/users" => "registrations#create", :as => :user_registration post "/users" => "registrations#create", :as => :user_registration
get "/registrations_closed" => "registrations#registrations_closed", :as => :registrations_closed
end end
get "users/invitations" => "invitations#new", :as => "new_user_invitation" get "users/invitations" => "invitations#new", :as => "new_user_invitation"

View file

@ -2,10 +2,12 @@
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration[5.1] class AddDeviseTwoFactorToUsers < ActiveRecord::Migration[5.1]
def change def change
add_column :users, :encrypted_otp_secret, :string change_table :users, bulk: true do |t|
add_column :users, :encrypted_otp_secret_iv, :string t.string :encrypted_otp_secret
add_column :users, :encrypted_otp_secret_salt, :string t.string :encrypted_otp_secret_iv
add_column :users, :consumed_timestep, :integer t.string :encrypted_otp_secret_salt
add_column :users, :otp_required_for_login, :boolean t.integer :consumed_timestep
t.boolean :otp_required_for_login
end
end end
end end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
class FixMissingRemotePhotoFields < ActiveRecord::Migration[5.1]
def up
Photo.where(remote_photo_path: nil).each do |photo|
photo.write_attribute(:unprocessed_image, photo.read_attribute(:processed_image))
photo.update_remote_path
photo.save!
end
end
end

View file

@ -0,0 +1,52 @@
# frozen_string_literal: true
class DecryptTwoFactorSecret < ActiveRecord::Migration[5.1]
class User < ApplicationRecord
end
def up
add_column :users, :plain_otp_secret, :string
key = twofa_encryption_key
decrypt_existing_secrets(key) if key
change_table :users, bulk: true do |t|
t.remove :encrypted_otp_secret
t.remove :encrypted_otp_secret_iv
t.remove :encrypted_otp_secret_salt
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
private
def twofa_encryption_key
if AppConfig.heroku?
ENV["TWOFA_ENCRYPTION_KEY"]
else
key_file = File.expand_path("../../config/initializers/twofa_encryption_key.rb", File.dirname(__FILE__))
if File.exist? key_file
require key_file
File.delete(key_file)
return Diaspora::Application.config.twofa_encryption_key
end
end
end
def decrypt_existing_secrets(key)
User.where.not(encrypted_otp_secret: nil).each do |user|
user.plain_otp_secret = Encryptor.decrypt(
value: user.encrypted_otp_secret.unpack("m").first,
key: key,
iv: user.encrypted_otp_secret_iv.unpack("m").first,
salt: user.encrypted_otp_secret_salt.slice(1..-1).unpack("m").first
)
user.save!
end
end
end

View file

@ -98,11 +98,11 @@ Feature: new user registration
Then I should not be able to sign up Then I should not be able to sign up
And I should have a validation error on "user_password, user_password_confirmation" And I should have a validation error on "user_password, user_password_confirmation"
Scenario: User signs up with an already existing username and email and then tries to sign in (Issue #6136) Scenario: User signs up with an already existing username and email and then tries to sign in
When I log out manually When I log out manually
And I go to the new user registration page And I go to the new user registration page
And I fill in the new user form with an existing email and username And I fill in the new user form
And I submit the form And I submit the form
Then I should see a flash message indicating failure Then I should see a flash message indicating failure
When I click the sign in button When I follow "Sign in"
Then I should not see a flash message indicating failure Then I should not see a flash message indicating failure

View file

@ -0,0 +1,24 @@
@javascript
Feature: New user registration
In order to use Diaspora*
As a desktop user
I want to register an account
Scenario: user signs up and goes to getting started
Given I am on the new user registration page
When I fill in the new user form
And I press "Create account"
Then I should be on the getting started page
And I should see the 'getting started' contents
Scenario: registrations are closed, user is informed
Given the registrations are closed
When I am on the new user registration page
Then I should see "Open signups are closed at this time"
Scenario: User is unable to register even by manually sending the POST request
Given I am on the new user registration page
When I fill in the new user form
Given the registrations are closed
When I press "Create account"
Then I should see "Open signups are closed at this time"

View file

@ -5,8 +5,7 @@ Feature: New user registration
I want to register an account I want to register an account
Background: Background:
Given I am on the login page Given I am on the new user registration page
And I follow "Create account" within "#main-nav"
Scenario: user signs up and goes to getting started Scenario: user signs up and goes to getting started
When I fill in the new user form When I fill in the new user form

View file

@ -74,3 +74,11 @@ end
Then (/^I should see the 'getting started' contents$/) do Then (/^I should see the 'getting started' contents$/) do
confirm_getting_started_contents confirm_getting_started_contents
end end
Given /^the registrations are closed$/ do
AppConfig.settings.enable_registrations = false
end
When /^I fill in the new user form$/ do
fill_in_new_user_form
end

View file

@ -218,20 +218,12 @@ When /^I view "([^\"]*)"'s first post$/ do |email|
visit post_path(post) visit post_path(post)
end end
When /^I fill in the new user form/ do
fill_in_new_user_form
end
And /^I should be able to friend "([^\"]*)"$/ do |email| And /^I should be able to friend "([^\"]*)"$/ do |email|
user = User.find_by_email(email) user = User.find_by_email(email)
step 'I should see a ".aspect-dropdown"' step 'I should see a ".aspect-dropdown"'
step "I should see \"#{user.name}\"" step "I should see \"#{user.name}\""
end end
When /^I click the sign in button$/ do
click_link "Sign in"
end
Given /^I did request my photos$/ do Given /^I did request my photos$/ do
@me.perform_export_photos! @me.perform_export_photos!
end end

View file

@ -68,24 +68,6 @@ module Configuration
end end
end end
def twofa_encryption_key
if heroku?
return ENV["TWOFA_ENCRYPTION_KEY"] if ENV["TWOFA_ENCRYPTION_KEY"]
warn "FATAL: Running on Heroku with TWOFA_ENCRYPTION_KEY unset"
warn " Run heroku config:add TWOFA_ENCRYPTION_KEY=#{SecureRandom.hex(32)}"
abort
else
key_file = File.expand_path(
"../config/initializers/twofa_encryption_key.rb",
File.dirname(__FILE__)
)
system "DISABLE_SPRING=1 bin/rake generate:twofa_key" unless File.exist? key_file
require key_file
Diaspora::Application.config.twofa_encryption_key
end
end
def version_string def version_string
return @version_string unless @version_string.nil? return @version_string unless @version_string.nil?

View file

@ -1,24 +0,0 @@
# frozen_string_literal: true
namespace :generate do
desc "Generates a key for encrypting 2fa tokens"
task :twofa_key do
path = Rails.root.join("config", "initializers", "twofa_encryption_key.rb")
key = SecureRandom.hex(32)
File.open(path, "w") do |f|
f.write <<~CONF
# frozen_string_literal: true
# The 2fa encryption key is used to encrypt users' OTP tokens in the database.
# You can regenerate this key by running `rake generate:twofa_key`
# If you change this key after a user has set up 2fa
# the users' tokens cannot be recovered
# and they will not be able to log in again!
Diaspora::Application.config.twofa_encryption_key = "#{key}"
CONF
end
end
end

View file

@ -25,16 +25,14 @@ describe RegistrationsController, type: :controller do
AppConfig.settings.enable_registrations = false AppConfig.settings.enable_registrations = false
end end
it "redirects #new to the login page" do it "redirects #new to the registrations closed page" do
get :new get :new
expect(flash[:error]).to eq(I18n.t("registrations.closed")) expect(response).to redirect_to registrations_closed_path
expect(response).to redirect_to new_user_session_path
end end
it "redirects #create to the login page" do it "redirects #create to the registrations closed page" do
post :create, params: valid_params post :create, params: valid_params
expect(flash[:error]).to eq(I18n.t("registrations.closed")) expect(response).to redirect_to registrations_closed_path
expect(response).to redirect_to new_user_session_path
end end
it "does not redirect if there is a valid invite token" do it "does not redirect if there is a valid invite token" do
@ -45,7 +43,8 @@ describe RegistrationsController, type: :controller do
it "does redirect if there is an invalid invite token" do it "does redirect if there is an invalid invite token" do
get :new, params: {invite: {token: "fssdfsd"}} get :new, params: {invite: {token: "fssdfsd"}}
expect(response).to redirect_to new_user_session_path expect(flash[:error]).to eq(I18n.t("registrations.invalid_invite"))
expect(response).to redirect_to registrations_closed_path
end end
it "does redirect if there are no invites available with this code" do it "does redirect if there are no invites available with this code" do
@ -53,7 +52,7 @@ describe RegistrationsController, type: :controller do
code.update_attributes(count: 0) code.update_attributes(count: 0)
get :new, params: {invite: {token: code.token}} get :new, params: {invite: {token: code.token}}
expect(response).to redirect_to new_user_session_path expect(response).to redirect_to registrations_closed_path
end end
it "does redirect when invitations are closed now" do it "does redirect when invitations are closed now" do
@ -61,7 +60,7 @@ describe RegistrationsController, type: :controller do
AppConfig.settings.invitations.open = false AppConfig.settings.invitations.open = false
get :new, params: {invite: {token: code.token}} get :new, params: {invite: {token: code.token}}
expect(response).to redirect_to new_user_session_path expect(response).to redirect_to registrations_closed_path
end end
it "does not redirect when the registration is open" do it "does not redirect when the registration is open" do

View file

@ -984,9 +984,7 @@ describe User, :type => :model do
exported_at exported_at
exported_photos_at exported_photos_at
consumed_timestep consumed_timestep
encrypted_otp_secret plain_otp_secret
encrypted_otp_secret_iv
encrypted_otp_secret_salt
otp_backup_codes otp_backup_codes
otp_required_for_login otp_required_for_login
otp_secret otp_secret