maestrano/mno-enterprise

View on GitHub
core/lib/mno_enterprise/concerns/controllers/auth/confirmations_controller.rb

Summary

Maintainability
B
5 hrs
Test Coverage
module MnoEnterprise::Concerns::Controllers::Auth::ConfirmationsController
  extend ActiveSupport::Concern

  #==================================================================
  # Included methods
  #==================================================================
  # 'included do' causes the included code to be evaluated in the
  # context where it is included rather than being executed in the module's context
  included do
    before_filter :signed_in_and_unconfirmed, only: [:lounge,:update]

    private
      # Redirects unless user is signed in and not confirmed yet
      def signed_in_and_unconfirmed
        resource = resource_class.to_adapter.get((send(:"current_#{resource_name}") || MnoEnterprise::User.new).to_key)
        return true if resource && !resource.confirmed?

        redirect_to MnoEnterprise.router.dashboard_path
        return false
      end
  end

  #==================================================================
  # Class methods
  #==================================================================
  module ClassMethods
    # def some_class_method
    #   'some text'
    # end
  end

  #==================================================================
  # Instance methods
  #==================================================================
  # GET /resource/confirmation/new
  # def new
  #   super
  # end

  # POST /resource/confirmation
  # def create
  #   super
  # end

  # GET /resource/confirmation?confirmation_token=abcdef
  # Override to display a form for the user to fill the final registration details
  def show
    @confirmation_token = params[:confirmation_token]
    self.resource = resource_class.find_for_confirmation(@confirmation_token)

    # Exit if no resources
    unless resource.errors.empty?
      yield(:error, resource) if block_given?
      respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
      return
    end

    # Case 1: user is confirmed but trying to confirm a new email address (change of email)
    # Case 2: user is a new user - in this case a form is displayed with final details to fill
    # Case 3: user is confirmed and clicking again on the link
    if resource.confirmed?
      resource.perform_confirmation(@confirmation_token)

      if resource.errors.empty?
        sign_in(resource)
        set_flash_message(:notice, :confirmed) if is_flashing_format?
        yield(:reconfirmation_success, resource) if block_given?
        respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
      else
        respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
      end
      return
    end

    # Check if phone number should be required
    # Bypassed for invited users
    @phone_required = resource.organizations.map(&:users).flatten.count == 1
    yield(:success, resource) if block_given?
  end

  # POST /resource/confirmation/finalize
  # Confirm a new user and update
  def finalize
    @confirmation_token = params[:user].delete(:confirmation_token)
    self.resource = resource_class.find_for_confirmation(@confirmation_token)

    # Exit action and redirect if user is already confirmed
    if resource && resource.confirmed?
      yield(:already_confirmed, resource) if block_given?
      redirect_to after_confirmation_path_for(resource_name, resource)
      return
    end

    if resource.errors.empty?
      if params[:tos] == "accept"
        params[:user][:meta_data] = resource.meta_data.merge(tos_accepted_at: Time.current)
      end
      resource.assign_attributes(params[:user]) unless resource.confirmed?
      resource.perform_confirmation(@confirmation_token)
      resource.save
      sign_in resource, bypass: true
      set_flash_message(:notice, :confirmed) if is_flashing_format?
      yield(:success,resource) if block_given?
      MnoEnterprise::EventLogger.info('user_confirm', resource.id, 'User confirmed', resource)
      respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource, new_user: true) }
    else
      yield(:error,resource) if block_given?
      respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
    end
  end

  # TODO: specs
  # GET /resource/confirmation/lounge
  def lounge
    self.resource = @resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    yield(:success,resource) if block_given?
  end

  # TODO: specs
  # PUT /resource/confirmation
  def update
    self.resource = @resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)

    # Redirect straight away if no changes
    if @resource.email == params[:user][:email]
      @resource.resend_confirmation_instructions
      redirect_to mno_enterprise.user_confirmation_lounge_path, notice: "The confirmation email has been resent."
      return
    end

    # Update email
    previous_email = @resource.email
    @resource.email = params[:user][:email]
    @resource.skip_reconfirmation!

    if @resource.save
      @resource.resend_confirmation_instructions
      yield(:success,resource) if block_given?
      redirect_to mno_enterprise.user_confirmation_lounge_path, notice: "'Email updated! A confirmation email has been resent."
    else
      # Rollback
      #@resource.restore_email!
      yield(resource,:error) if block_given?
      render 'lounge'
    end
  end

  protected
    # The path used after resending confirmation instructions.
    # def after_resending_confirmation_instructions_path_for(resource_name)
    #   super(resource_name)
    # end

    # The path used after confirmation.
    # Confirm any outstanding organization invite
    # TODO: invite acceptance logic should be moved to the 'show' action
    def after_confirmation_path_for(resource_name, resource, opts = {})
      return new_user_session_path unless resource

      # 3 days is the duration of an invite.
      if resource.created_at > 3.days.ago
        # First auto confirm the orga invite if user has pending
        # invites
        # Get invites from previous_url (user was accepting invite but didn't have an account)
        org_invites = []
        if !session[:previous_url].blank? && (r = session[:previous_url].match(/\/org_invites\/(\d+)\?token=(\w+)/))
          invite_params = { id: r.captures[0].to_i, token: r.captures[1] }
          org_invites << MnoEnterprise::OrgInvite.where(invite_params).first
        end

        # Get remaining invites via email address
        org_invites << MnoEnterprise::OrgInvite.where(user_email: resource.email).to_a
        org_invites.flatten!
        org_invites.uniq!

        # Accept the invites
        org_invites.each do |org_invite|
          org_invite.accept!(resource) unless org_invite.expired?
        end
      end

      new_user_signed_in_session_path(resource, opts)
    end

    def new_user_signed_in_session_path(resource, opts)
      if MnoEnterprise.style.workflow.signup_onboarding && opts[:new_user]
        warn '[DEPRECATION] Onboarding workflow is deprecated.'
        after_sign_in_path_for(resource)
      elsif opts[:new_user]
        after_sign_in_path_for(resource)
      else
        signed_in_root_path(resource)
      end
    end
end