From 5a162c6f4ff923c2d8c1faddbbafdd0a2e4dd4f9 Mon Sep 17 00:00:00 2001 From: Riccardo Graziosi <31478034+riggraz@users.noreply.github.com> Date: Tue, 14 May 2024 17:47:17 +0200 Subject: [PATCH] Add instructions to set password for OAuth users (#346) --- app/controllers/passwords_controller.rb | 19 +++++++++ app/controllers/registrations_controller.rb | 10 +++++ app/controllers/tenants_controller.rb | 1 + app/javascript/components/Board/NewPost.tsx | 2 +- .../UserProfile/SetPasswordDialog.tsx | 41 +++++++++++++++++++ app/javascript/components/common/Button.tsx | 4 +- app/views/devise/registrations/edit.html.erb | 22 +++++++++- app/workflows/o_auth_sign_in_user_workflow.rb | 1 + config/locales/en.yml | 5 ++- config/routes.rb | 7 +++- ...514112836_add_has_set_password_to_users.rb | 5 +++ db/schema.rb | 3 +- spec/system/user_edit_profile_spec.rb | 5 +-- 13 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 app/controllers/passwords_controller.rb create mode 100644 app/javascript/components/UserProfile/SetPasswordDialog.tsx create mode 100644 db/migrate/20240514112836_add_has_set_password_to_users.rb diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 000000000..552813995 --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,19 @@ +class PasswordsController < Devise::PasswordsController + # Needed to have Current.tenant available in Devise's controllers + prepend_before_action :load_tenant_data + + # Needed for OAuth users (otherwise an already logged in user wouldn't be able to reset their password) + skip_before_action :require_no_authentication, :only => [:edit, :update] + + def update + super + + if resource.errors.empty? + resource.update!(has_set_password: true) unless resource.has_set_password? + + # See: https://stackoverflow.com/a/22110985/1857435 + sign_out(resource_name) + sign_in(resource_name, resource) + end + end +end \ No newline at end of file diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 43a8699af..11e59088a 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -17,6 +17,16 @@ def destroy respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) } end + def send_set_password_instructions + user = User.find_by_email(params[:email]) + + if user.present? + user.send_reset_password_instructions + end + + render json: { success: true } # always return true, even if user not found + end + private def set_page_title_new diff --git a/app/controllers/tenants_controller.rb b/app/controllers/tenants_controller.rb index dbf97a5de..c1cd27328 100644 --- a/app/controllers/tenants_controller.rb +++ b/app/controllers/tenants_controller.rb @@ -42,6 +42,7 @@ def create full_name: params[:user][:full_name] || I18n.t('defaults.user_full_name'), email: params[:user][:email], password: is_o_auth_login ? Devise.friendly_token : params[:user][:password], + has_set_password: !is_o_auth_login, role: "owner" ) diff --git a/app/javascript/components/Board/NewPost.tsx b/app/javascript/components/Board/NewPost.tsx index 328526aff..f0ab0510d 100644 --- a/app/javascript/components/Board/NewPost.tsx +++ b/app/javascript/components/Board/NewPost.tsx @@ -125,7 +125,7 @@ class NewPost extends React.Component { } catch (e) { this.setState({ - error: I18n.t('board.new_post.submit_error') + error: I18n.t('common.errors.unknown') }); } } diff --git a/app/javascript/components/UserProfile/SetPasswordDialog.tsx b/app/javascript/components/UserProfile/SetPasswordDialog.tsx new file mode 100644 index 000000000..50f43d0f3 --- /dev/null +++ b/app/javascript/components/UserProfile/SetPasswordDialog.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import I18n from 'i18n-js'; + +import Button from '../common/Button'; +import buildRequestHeaders from '../../helpers/buildRequestHeaders'; + +interface Props { + sendSetPasswordInstructionsEndpoint: string; + authenticityToken: string; +} + +const SetPasswordDialog = ({ sendSetPasswordInstructionsEndpoint, authenticityToken }: Props) => { + + + return ( +
+

+ { I18n.t('common.forms.auth.no_password_set') } +

+ + +
+ ) +}; + +export default SetPasswordDialog; diff --git a/app/javascript/components/common/Button.tsx b/app/javascript/components/common/Button.tsx index f9ce62be3..f98caf702 100644 --- a/app/javascript/components/common/Button.tsx +++ b/app/javascript/components/common/Button.tsx @@ -3,14 +3,16 @@ import * as React from 'react'; interface Props { children: any; onClick(e: React.FormEvent): void; + type?: 'button' | 'submit'; className?: string; outline?: boolean; disabled?: boolean; } -const Button = ({ children, onClick, className = '', outline = false, disabled = false}: Props) => ( +const Button = ({ children, onClick, type="submit", className = '', outline = false, disabled = false}: Props) => (