* Cas authentication feature * Config * Remove class_eval + Omniauth initializer * Codeclimate review * Codeclimate review 2 * Codeclimate review 3 * Remove uid/email reconciliation * SAML authentication * Clean up code * Improve login form * Fix code style issues * Add localesmaster
@@ -13,7 +13,7 @@ DB_PORT=5432 | |||||
# Federation | # Federation | ||||
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation. | # Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation. | ||||
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. | # LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. | ||||
LOCAL_DOMAIN=example.com | |||||
LOCAL_DOMAIN=example.com | |||||
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links) | # Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links) | ||||
@@ -58,7 +58,7 @@ VAPID_PUBLIC_KEY= | |||||
# E-mail configuration | # E-mail configuration | ||||
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers | # Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers | ||||
# If you want to use an SMTP server without authentication (e.g local Postfix relay) | # If you want to use an SMTP server without authentication (e.g local Postfix relay) | ||||
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and | |||||
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and | |||||
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough). | # *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough). | ||||
SMTP_SERVER=smtp.mailgun.org | SMTP_SERVER=smtp.mailgun.org | ||||
SMTP_PORT=587 | SMTP_PORT=587 | ||||
@@ -135,3 +135,43 @@ STREAMING_CLUSTER_NUM=1 | |||||
# If you use Docker, you may want to assign UID/GID manually. | # If you use Docker, you may want to assign UID/GID manually. | ||||
# UID=1000 | # UID=1000 | ||||
# GID=1000 | # GID=1000 | ||||
# Optional CAS authentication (cf. omniauth-cas) : | |||||
# CAS_ENABLED=true | |||||
# CAS_URL=https://sso.myserver.com/ | |||||
# CAS_HOST=sso.myserver.com/ | |||||
# CAS_PORT=443 | |||||
# CAS_SSL=true | |||||
# CAS_VALIDATE_URL= | |||||
# CAS_CALLBACK_URL= | |||||
# CAS_LOGOUT_URL= | |||||
# CAS_LOGIN_URL= | |||||
# CAS_UID_FIELD='user' | |||||
# CAS_CA_PATH= | |||||
# CAS_DISABLE_SSL_VERIFICATION=false | |||||
# CAS_UID_KEY='user' | |||||
# CAS_NAME_KEY='name' | |||||
# CAS_EMAIL_KEY='email' | |||||
# CAS_NICKNAME_KEY='nickname' | |||||
# CAS_FIRST_NAME_KEY='firstname' | |||||
# CAS_LAST_NAME_KEY='lastname' | |||||
# CAS_LOCATION_KEY='location' | |||||
# CAS_IMAGE_KEY='image' | |||||
# CAS_PHONE_KEY='phone' | |||||
# Optional SAML authentication (cf. omniauth-saml) | |||||
# SAML_ENABLED=true | |||||
# SAML_ACS_URL= | |||||
# SAML_ISSUER=http://localhost:3000/auth/auth/saml/metadata | |||||
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO | |||||
# SAML_IDP_CERT= | |||||
# SAML_IDP_CERT_FINGERPRINT= | |||||
# SAML_NAME_IDENTIFIER_FORMAT= | |||||
# SAML_CERT= | |||||
# SAML_PRIVATE_KEY= | |||||
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true | |||||
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true | |||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1" | |||||
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" | |||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42" | |||||
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1" |
@@ -32,6 +32,9 @@ gem 'devise', '~> 4.4' | |||||
gem 'devise-two-factor', '~> 3.0' | gem 'devise-two-factor', '~> 3.0' | ||||
gem 'devise_pam_authenticatable2', '~> 8.0' | gem 'devise_pam_authenticatable2', '~> 8.0' | ||||
gem 'omniauth-cas', '~> 1.1', install_if: -> { ENV['CAS_ENABLED'] == 'true' } | |||||
gem 'omniauth-saml', '~> 1.8', install_if: -> { ENV['SAML_ENABLED'] == 'true' } | |||||
gem 'omniauth', '~> 1.2' | |||||
gem 'doorkeeper', '~> 4.2' | gem 'doorkeeper', '~> 4.2' | ||||
gem 'fast_blank', '~> 1.0' | gem 'fast_blank', '~> 1.0' | ||||
@@ -201,6 +201,7 @@ GEM | |||||
hamster (3.0.0) | hamster (3.0.0) | ||||
concurrent-ruby (~> 1.0) | concurrent-ruby (~> 1.0) | ||||
hashdiff (0.3.7) | hashdiff (0.3.7) | ||||
hashie (3.5.7) | |||||
highline (1.7.10) | highline (1.7.10) | ||||
hiredis (0.6.1) | hiredis (0.6.1) | ||||
hkdf (0.3.0) | hkdf (0.3.0) | ||||
@@ -304,6 +305,16 @@ GEM | |||||
sidekiq (>= 3.5.0) | sidekiq (>= 3.5.0) | ||||
statsd-ruby (~> 1.2.0) | statsd-ruby (~> 1.2.0) | ||||
oj (3.3.10) | oj (3.3.10) | ||||
omniauth (1.8.1) | |||||
hashie (>= 3.4.6, < 3.6.0) | |||||
rack (>= 1.6.2, < 3) | |||||
omniauth-cas (1.1.1) | |||||
addressable (~> 2.3) | |||||
nokogiri (~> 1.5) | |||||
omniauth (~> 1.2) | |||||
omniauth-saml (1.9.0) | |||||
omniauth (~> 1.3, >= 1.3.2) | |||||
ruby-saml (~> 1.4, >= 1.4.3) | |||||
orm_adapter (0.5.0) | orm_adapter (0.5.0) | ||||
ostatus2 (2.0.3) | ostatus2 (2.0.3) | ||||
addressable (~> 2.5) | addressable (~> 2.5) | ||||
@@ -455,6 +466,8 @@ GEM | |||||
unicode-display_width (~> 1.0, >= 1.0.1) | unicode-display_width (~> 1.0, >= 1.0.1) | ||||
ruby-oembed (0.12.0) | ruby-oembed (0.12.0) | ||||
ruby-progressbar (1.9.0) | ruby-progressbar (1.9.0) | ||||
ruby-saml (1.6.1) | |||||
nokogiri (>= 1.5.10) | |||||
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) | ||||
@@ -606,6 +619,9 @@ DEPENDENCIES | |||||
nokogiri (~> 1.8) | nokogiri (~> 1.8) | ||||
nsa (~> 0.2) | nsa (~> 0.2) | ||||
oj (~> 3.3) | oj (~> 3.3) | ||||
omniauth (~> 1.2) | |||||
omniauth-cas (~> 1.1) | |||||
omniauth-saml (~> 1.8) | |||||
ostatus2 (~> 2.0) | ostatus2 (~> 2.0) | ||||
ox (~> 2.8) | ox (~> 2.8) | ||||
paperclip (~> 5.1) | paperclip (~> 5.1) | ||||
@@ -2,4 +2,28 @@ | |||||
class Auth::ConfirmationsController < Devise::ConfirmationsController | class Auth::ConfirmationsController < Devise::ConfirmationsController | ||||
layout 'auth' | layout 'auth' | ||||
before_action :set_user, only: [:finish_signup] | |||||
# GET/PATCH /users/:id/finish_signup | |||||
def finish_signup | |||||
return unless request.patch? && params[:user] | |||||
if @user.update(user_params) | |||||
@user.skip_reconfirmation! | |||||
sign_in(@user, bypass: true) | |||||
redirect_to root_path, notice: I18n.t('devise.confirmations.send_instructions') | |||||
else | |||||
@show_errors = true | |||||
end | |||||
end | |||||
private | |||||
def set_user | |||||
@user = current_user | |||||
end | |||||
def user_params | |||||
params.require(:user).permit(:email) | |||||
end | |||||
end | end |
@@ -0,0 +1,33 @@ | |||||
# frozen_string_literal: true | |||||
class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController | |||||
skip_before_action :verify_authenticity_token | |||||
def self.provides_callback_for(provider) | |||||
provider_id = provider.to_s.chomp '_oauth2' | |||||
define_method provider do | |||||
@user = User.find_for_oauth(request.env['omniauth.auth'], current_user) | |||||
if @user.persisted? | |||||
sign_in_and_redirect @user, event: :authentication | |||||
set_flash_message(:notice, :success, kind: provider_id.capitalize) if is_navigational_format? | |||||
else | |||||
session["devise.#{provider}_data"] = request.env['omniauth.auth'] | |||||
redirect_to new_user_registration_url | |||||
end | |||||
end | |||||
end | |||||
Devise.omniauth_configs.each_key do |provider| | |||||
provides_callback_for provider | |||||
end | |||||
def after_sign_in_path_for(resource) | |||||
if resource.email_verified? | |||||
root_path | |||||
else | |||||
finish_signup_path | |||||
end | |||||
end | |||||
end |
@@ -568,3 +568,21 @@ code { | |||||
margin-bottom: 4px; | margin-bottom: 4px; | ||||
} | } | ||||
} | } | ||||
.alternative-login { | |||||
margin-top: 20px; | |||||
margin-bottom: 20px; | |||||
h4 { | |||||
font-size: 16px; | |||||
color: $ui-base-lighter-color; | |||||
text-align: center; | |||||
margin-bottom: 20px; | |||||
border: 0; | |||||
padding: 0; | |||||
} | |||||
.button { | |||||
display: block; | |||||
} | |||||
} |
@@ -0,0 +1,81 @@ | |||||
# frozen_string_literal: true | |||||
module Omniauthable | |||||
extend ActiveSupport::Concern | |||||
TEMP_EMAIL_PREFIX = 'change@me' | |||||
TEMP_EMAIL_REGEX = /\Achange@me/ | |||||
included do | |||||
def omniauth_providers | |||||
Devise.omniauth_configs.keys | |||||
end | |||||
def email_verified? | |||||
email && email !~ TEMP_EMAIL_REGEX | |||||
end | |||||
end | |||||
class_methods do | |||||
def find_for_oauth(auth, signed_in_resource = nil) | |||||
# EOLE-SSO Patch | |||||
auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array | |||||
identity = Identity.find_for_oauth(auth) | |||||
# If a signed_in_resource is provided it always overrides the existing user | |||||
# to prevent the identity being locked with accidentally created accounts. | |||||
# Note that this may leave zombie accounts (with no associated identity) which | |||||
# can be cleaned up at a later date. | |||||
user = signed_in_resource ? signed_in_resource : identity.user | |||||
user = create_for_oauth(auth) if user.nil? | |||||
if identity.user.nil? | |||||
identity.user = user | |||||
identity.save! | |||||
end | |||||
user | |||||
end | |||||
def create_for_oauth(auth) | |||||
# Check if the user exists with provided email if the provider gives us a | |||||
# verified email. If no verified email was provided or the user already | |||||
# exists, we assign a temporary email and ask the user to verify it on | |||||
# the next step via Auth::ConfirmationsController.finish_signup | |||||
user = User.new(user_params_from_auth(auth)) | |||||
user.account.avatar_remote_url = auth.info.image if auth.info.image =~ /\A#{URI.regexp(%w(http https))}\z/ | |||||
user.skip_confirmation! | |||||
user.save! | |||||
user | |||||
end | |||||
private | |||||
def user_params_from_auth(auth) | |||||
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email) | |||||
email = auth.info.email if email_is_verified && !User.exists?(email: auth.info.email) | |||||
{ | |||||
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com", | |||||
password: Devise.friendly_token[0, 20], | |||||
account_attributes: { | |||||
username: ensure_unique_username(auth.uid), | |||||
display_name: [auth.info.first_name, auth.info.last_name].join(' '), | |||||
}, | |||||
} | |||||
end | |||||
def ensure_unique_username(starting_username) | |||||
username = starting_username | |||||
i = 0 | |||||
while Account.exists?(username: username) | |||||
i += 1 | |||||
username = "#{starting_username}_#{i}" | |||||
end | |||||
username | |||||
end | |||||
end | |||||
end |
@@ -0,0 +1,22 @@ | |||||
# frozen_string_literal: true | |||||
# == Schema Information | |||||
# | |||||
# Table name: identities | |||||
# | |||||
# id :integer not null, primary key | |||||
# user_id :integer | |||||
# provider :string default(""), not null | |||||
# uid :string default(""), not null | |||||
# created_at :datetime not null | |||||
# updated_at :datetime not null | |||||
# | |||||
class Identity < ApplicationRecord | |||||
belongs_to :user, dependent: :destroy | |||||
validates :uid, presence: true, uniqueness: { scope: :provider } | |||||
validates :provider, presence: true | |||||
def self.find_for_oauth(auth) | |||||
find_or_create_by(uid: auth.uid, provider: auth.provider) | |||||
end | |||||
end |
@@ -39,6 +39,7 @@ | |||||
class User < ApplicationRecord | class User < ApplicationRecord | ||||
include Settings::Extend | include Settings::Extend | ||||
include Omniauthable | |||||
ACTIVE_DURATION = 14.days | ACTIVE_DURATION = 14.days | ||||
@@ -52,6 +53,7 @@ class User < ApplicationRecord | |||||
:confirmable | :confirmable | ||||
devise :pam_authenticatable | devise :pam_authenticatable | ||||
devise :omniauthable | |||||
belongs_to :account, inverse_of: :user | belongs_to :account, inverse_of: :user | ||||
belongs_to :invite, counter_cache: :uses, optional: true | belongs_to :invite, counter_cache: :uses, optional: true | ||||
@@ -0,0 +1,14 @@ | |||||
- content_for :page_title do | |||||
= t('auth.confirm_email') | |||||
= simple_form_for(current_user, as: 'user', url: finish_signup_path, html: { role: 'form'}) do |f| | |||||
- if @show_errors && current_user.errors.any? | |||||
#error_explanation | |||||
- current_user.errors.full_messages.each do |msg| | |||||
= msg | |||||
%br | |||||
= f.input :email | |||||
.actions | |||||
= f.submit t('auth.confirm_email'), class: 'button' |
@@ -14,4 +14,13 @@ | |||||
.actions | .actions | ||||
= f.button :button, t('auth.login'), type: :submit | = f.button :button, t('auth.login'), type: :submit | ||||
- if devise_mapping.omniauthable? and resource_class.omniauth_providers.any? | |||||
.simple_form.alternative-login | |||||
%h4= t('auth.or_log_in_with') | |||||
.actions | |||||
- resource_class.omniauth_providers.each do |provider| | |||||
= link_to omniauth_authorize_path(resource_name, provider), class: "button button-#{provider}" do | |||||
= t("auth.providers.#{provider}", default: provider.to_s.chomp("_oauth2").capitalize) | |||||
.form-footer= render 'auth/shared/links' | .form-footer= render 'auth/shared/links' |
@@ -46,6 +46,7 @@ ignore_missing: | |||||
- 'terms.body_html' | - 'terms.body_html' | ||||
- 'application_mailer.salutation' | - 'application_mailer.salutation' | ||||
- 'errors.500' | - 'errors.500' | ||||
- 'auth.providers.*' | |||||
ignore_unused: | ignore_unused: | ||||
- 'activemodel.errors.*' | - 'activemodel.errors.*' | ||||
@@ -0,0 +1,59 @@ | |||||
Rails.application.config.middleware.use OmniAuth::Builder do | |||||
# Vanilla omniauth stategies | |||||
end | |||||
Devise.setup do |config| | |||||
# Devise omniauth strategies | |||||
# CAS strategy | |||||
if ENV['CAS_ENABLED'] == 'true' | |||||
cas_options = {} | |||||
cas_options[:url] = ENV['CAS_URL'] if ENV['CAS_URL'] | |||||
cas_options[:host] = ENV['CAS_HOST'] if ENV['CAS_HOST'] | |||||
cas_options[:port] = ENV['CAS_PORT'] if ENV['CAS_PORT'] | |||||
cas_options[:ssl] = ENV['CAS_SSL'] == 'true' if ENV['CAS_SSL'] | |||||
cas_options[:validate_url] = ENV['CAS_VALIDATE_URL'] if ENV['CAS_VALIDATE_URL'] | |||||
cas_options[:callback_url] = ENV['CAS_CALLBACK_URL'] if ENV['CAS_CALLBACK_URL'] | |||||
cas_options[:logout_url] = ENV['CAS_LOGOUT_URL'] if ENV['CAS_LOGOUT_URL'] | |||||
cas_options[:login_url] = ENV['CAS_LOGIN_URL'] if ENV['CAS_LOGIN_URL'] | |||||
cas_options[:uid_field] = ENV['CAS_UID_FIELD'] || 'user' if ENV['CAS_UID_FIELD'] | |||||
cas_options[:ca_path] = ENV['CAS_CA_PATH'] if ENV['CAS_CA_PATH'] | |||||
cas_options[:disable_ssl_verification] = ENV['CAS_DISABLE_SSL_VERIFICATION'] == 'true' if ENV['CAS_DISABLE_SSL_VERIFICATION'] | |||||
cas_options[:uid_key] = ENV['CAS_UID_KEY'] || 'user' | |||||
cas_options[:name_key] = ENV['CAS_NAME_KEY'] || 'name' | |||||
cas_options[:email_key] = ENV['CAS_EMAIL_KEY'] || 'email' | |||||
cas_options[:nickname_key] = ENV['CAS_NICKNAME_KEY'] || 'nickname' | |||||
cas_options[:first_name_key] = ENV['CAS_FIRST_NAME_KEY'] || 'firstname' | |||||
cas_options[:last_name_key] = ENV['CAS_LAST_NAME_KEY'] || 'lastname' | |||||
cas_options[:location_key] = ENV['CAS_LOCATION_KEY'] || 'location' | |||||
cas_options[:image_key] = ENV['CAS_IMAGE_KEY'] || 'image' | |||||
cas_options[:phone_key] = ENV['CAS_PHONE_KEY'] || 'phone' | |||||
config.omniauth :cas, cas_options | |||||
end | |||||
# SAML strategy | |||||
if ENV['SAML_ENABLED'] == 'true' | |||||
saml_options = {} | |||||
saml_options[:assertion_consumer_service_url] = ENV['SAML_ACS_URL'] if ENV['SAML_ACS_URL'] | |||||
saml_options[:issuer] = ENV['SAML_ISSUER'] if ENV['SAML_ISSUER'] | |||||
saml_options[:idp_sso_target_url] = ENV['SAML_IDP_SSO_TARGET_URL'] if ENV['SAML_IDP_SSO_TARGET_URL'] | |||||
saml_options[:idp_sso_target_url_runtime_params] = ENV['SAML_IDP_SSO_TARGET_PARAMS'] if ENV['SAML_IDP_SSO_TARGET_PARAMS'] # FIXME: Should be parsable Hash | |||||
saml_options[:idp_cert] = ENV['SAML_IDP_CERT'] if ENV['SAML_IDP_CERT'] | |||||
saml_options[:idp_cert_fingerprint] = ENV['SAML_IDP_CERT_FINGERPRINT'] if ENV['SAML_IDP_CERT_FINGERPRINT'] | |||||
saml_options[:idp_cert_fingerprint_validator] = ENV['SAML_IDP_CERT_FINGERPRINT_VALIDATOR'] if ENV['SAML_IDP_CERT_FINGERPRINT_VALIDATOR'] # FIXME: Should be Lambda { |fingerprint| } | |||||
saml_options[:name_identifier_format] = ENV['SAML_NAME_IDENTIFIER_FORMAT'] if ENV['SAML_NAME_IDENTIFIER_FORMAT'] | |||||
saml_options[:request_attributes] = {} | |||||
saml_options[:certificate] = ENV['SAML_CERT'] if ENV['SAML_CERT'] | |||||
saml_options[:private_key] = ENV['SAML_PRIVATE_KEY'] if ENV['SAML_PRIVATE_KEY'] | |||||
saml_options[:security] = {} | |||||
saml_options[:security][:want_assertions_signed] = ENV['SAML_SECURITY_WANT_ASSERTION_SIGNED'] == 'true' | |||||
saml_options[:security][:want_assertions_encrypted] = ENV['SAML_SECURITY_WANT_ASSERTION_ENCRYPTED'] == 'true' | |||||
saml_options[:attribute_statements] = {} | |||||
saml_options[:attribute_statements][:uid] = [ENV['SAML_ATTRIBUTES_STATEMENTS_UID']] if ENV['SAML_ATTRIBUTES_STATEMENTS_UID'] | |||||
saml_options[:attribute_statements][:email] = [ENV['SAML_ATTRIBUTES_STATEMENTS_EMAIL']] if ENV['SAML_ATTRIBUTES_STATEMENTS_EMAIL'] | |||||
saml_options[:attribute_statements][:full_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_FULL_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_FULL_NAME'] | |||||
saml_options[:uid_attribute] = ENV['SAML_UID_ATTRIBUTE'] if ENV['SAML_UID_ATTRIBUTE'] | |||||
config.omniauth :saml, saml_options | |||||
end | |||||
end |
@@ -355,6 +355,7 @@ en: | |||||
auth: | auth: | ||||
agreement_html: By signing up you agree to follow <a href="%{rules_path}">the rules of the instance</a> and <a href="%{terms_path}">our terms of service</a>. | agreement_html: By signing up you agree to follow <a href="%{rules_path}">the rules of the instance</a> and <a href="%{terms_path}">our terms of service</a>. | ||||
change_password: Security | change_password: Security | ||||
confirm_email: Confirm email | |||||
delete_account: Delete account | delete_account: Delete account | ||||
delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation. | delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation. | ||||
didnt_get_confirmation: Didn't receive confirmation instructions? | didnt_get_confirmation: Didn't receive confirmation instructions? | ||||
@@ -364,6 +365,10 @@ en: | |||||
logout: Logout | logout: Logout | ||||
migrate_account: Move to a different account | migrate_account: Move to a different account | ||||
migrate_account_html: If you wish to redirect this account to a different one, you can <a href="%{path}">configure it here</a>. | migrate_account_html: If you wish to redirect this account to a different one, you can <a href="%{path}">configure it here</a>. | ||||
or_log_in_with: Or log in with | |||||
providers: | |||||
cas: CAS | |||||
saml: SAML | |||||
register: Sign up | register: Sign up | ||||
resend_confirmation: Resend confirmation instructions | resend_confirmation: Resend confirmation instructions | ||||
reset_password: Reset password | reset_password: Reset password | ||||
@@ -355,6 +355,7 @@ fr: | |||||
auth: | auth: | ||||
agreement_html: En vous inscrivant, vous souscrivez <a href="%{rules_path}">aux règles de l’instance</a> et à <a href="%{terms_path}">nos conditions d’utilisation</a>. | agreement_html: En vous inscrivant, vous souscrivez <a href="%{rules_path}">aux règles de l’instance</a> et à <a href="%{terms_path}">nos conditions d’utilisation</a>. | ||||
change_password: Sécurité | change_password: Sécurité | ||||
confirm_email: Confirmer mon adresse mail | |||||
delete_account: Supprimer le compte | delete_account: Supprimer le compte | ||||
delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action. | delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action. | ||||
didnt_get_confirmation: Vous n’avez pas reçu les consignes de confirmation ? | didnt_get_confirmation: Vous n’avez pas reçu les consignes de confirmation ? | ||||
@@ -364,6 +365,7 @@ fr: | |||||
logout: Se déconnecter | logout: Se déconnecter | ||||
migrate_account: Déplacer vers un compte différent | migrate_account: Déplacer vers un compte différent | ||||
migrate_account_html: Si vous voulez rediriger ce compte vers un autre, vous pouvez le <a href="%{path}">configurer ici</a>. | migrate_account_html: Si vous voulez rediriger ce compte vers un autre, vous pouvez le <a href="%{path}">configurer ici</a>. | ||||
or_log_in_with: Ou authentifiez-vous avec | |||||
register: S’inscrire | register: S’inscrire | ||||
resend_confirmation: Envoyer à nouveau les consignes de confirmation | resend_confirmation: Envoyer à nouveau les consignes de confirmation | ||||
reset_password: Réinitialiser le mot de passe | reset_password: Réinitialiser le mot de passe | ||||
@@ -24,9 +24,11 @@ Rails.application.routes.draw do | |||||
devise_scope :user do | devise_scope :user do | ||||
get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite | get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite | ||||
match '/auth/finish_signup' => 'auth/confirmations#finish_signup', via: [:get, :patch], as: :finish_signup | |||||
end | end | ||||
devise_for :users, path: 'auth', controllers: { | devise_for :users, path: 'auth', controllers: { | ||||
omniauth_callbacks: 'auth/omniauth_callbacks', | |||||
sessions: 'auth/sessions', | sessions: 'auth/sessions', | ||||
registrations: 'auth/registrations', | registrations: 'auth/registrations', | ||||
passwords: 'auth/passwords', | passwords: 'auth/passwords', | ||||
@@ -0,0 +1,11 @@ | |||||
class CreateIdentities < ActiveRecord::Migration[5.0] | |||||
def change | |||||
create_table :identities do |t| | |||||
t.references :user, foreign_key: { on_delete: :cascade } | |||||
t.string :provider, null: false, default: '' | |||||
t.string :uid, null: false, default: '' | |||||
t.timestamps | |||||
end | |||||
end | |||||
end |
@@ -10,7 +10,7 @@ | |||||
# | # | ||||
# It's strongly recommended that you check this file into your version control system. | # It's strongly recommended that you check this file into your version control system. | ||||
ActiveRecord::Schema.define(version: 20180109143959) do | |||||
ActiveRecord::Schema.define(version: 20180204034416) do | |||||
# These are extensions that must be enabled in order to support this database | # These are extensions that must be enabled in order to support this database | ||||
enable_extension "plpgsql" | enable_extension "plpgsql" | ||||
@@ -173,6 +173,15 @@ ActiveRecord::Schema.define(version: 20180109143959) do | |||||
t.index ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true | t.index ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true | ||||
end | end | ||||
create_table "identities", id: :serial, force: :cascade do |t| | |||||
t.integer "user_id" | |||||
t.string "provider", default: "", null: false | |||||
t.string "uid", default: "", null: false | |||||
t.datetime "created_at", null: false | |||||
t.datetime "updated_at", null: false | |||||
t.index ["user_id"], name: "index_identities_on_user_id" | |||||
end | |||||
create_table "imports", force: :cascade do |t| | create_table "imports", force: :cascade do |t| | ||||
t.integer "type", null: false | t.integer "type", null: false | ||||
t.boolean "approved", default: false, null: false | t.boolean "approved", default: false, null: false | ||||
@@ -526,6 +535,7 @@ ActiveRecord::Schema.define(version: 20180109143959) do | |||||
add_foreign_key "follow_requests", "accounts", name: "fk_76d644b0e7", on_delete: :cascade | add_foreign_key "follow_requests", "accounts", name: "fk_76d644b0e7", on_delete: :cascade | ||||
add_foreign_key "follows", "accounts", column: "target_account_id", name: "fk_745ca29eac", on_delete: :cascade | add_foreign_key "follows", "accounts", column: "target_account_id", name: "fk_745ca29eac", on_delete: :cascade | ||||
add_foreign_key "follows", "accounts", name: "fk_32ed1b5560", on_delete: :cascade | add_foreign_key "follows", "accounts", name: "fk_32ed1b5560", on_delete: :cascade | ||||
add_foreign_key "identities", "users", on_delete: :cascade | |||||
add_foreign_key "imports", "accounts", name: "fk_6db1b6e408", on_delete: :cascade | add_foreign_key "imports", "accounts", name: "fk_6db1b6e408", on_delete: :cascade | ||||
add_foreign_key "invites", "users", on_delete: :cascade | add_foreign_key "invites", "users", on_delete: :cascade | ||||
add_foreign_key "list_accounts", "accounts", on_delete: :cascade | add_foreign_key "list_accounts", "accounts", on_delete: :cascade | ||||
@@ -0,0 +1,5 @@ | |||||
Fabricator(:identity) do | |||||
user nil | |||||
provider "MyString" | |||||
uid "MyString" | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe Identity, type: :model do | |||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end |