diff --git a/rails/Gemfile b/rails/Gemfile index 3ee83a6e0..d1d32b1ce 100644 --- a/rails/Gemfile +++ b/rails/Gemfile @@ -30,17 +30,13 @@ gem 'rack-cors' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' - -# Use devise for authentication -gem 'devise', '~> 4.9.0' +gem 'bcrypt', '~> 3.1.7' # Use pundit for policy based authorization gem 'pundit', '~> 2.3.0' # Internationalization gem 'rails-i18n', '~> 7.0.3' -gem 'devise-i18n', '~> 1.11.0' # S3 backed Active Storage gem 'aws-sdk-s3', '~> 1.119.0' diff --git a/rails/Gemfile.lock b/rails/Gemfile.lock index c14043892..e8cb81b46 100644 --- a/rails/Gemfile.lock +++ b/rails/Gemfile.lock @@ -120,14 +120,6 @@ GEM railties (>= 6.0.0) date (3.3.4) debug_inspector (1.2.0) - devise (4.9.4) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 4.1.0) - responders - warden (~> 1.2.3) - devise-i18n (1.11.1) - devise (>= 4.9.0) diff-lcs (1.5.1) docile (1.4.0) erubi (1.12.0) @@ -205,7 +197,6 @@ GEM notiffany (0.1.3) nenv (~> 0.1) shellany (~> 0.0) - orm_adapter (0.5.0) pg (1.5.6) pry (0.14.2) coderay (~> 1.1) @@ -267,9 +258,6 @@ GEM railties (>= 3.2) tilt regexp_parser (2.9.0) - responders (3.1.1) - actionpack (>= 5.2) - railties (>= 5.2) rexml (3.2.6) rgeo (2.4.0) rgeo-geojson (2.1.1) @@ -332,8 +320,6 @@ GEM timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - warden (1.2.9) - rack (>= 2.0.9) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -358,14 +344,13 @@ DEPENDENCIES active_storage_validations (~> 1.0) annotate aws-sdk-s3 (~> 1.119.0) + bcrypt (~> 3.1.7) better_errors binding_of_caller bundler-audit byebug capybara (>= 2.15, < 4.0) cssbundling-rails (~> 1.1.2) - devise (~> 4.9.0) - devise-i18n (~> 1.11.0) factory_bot_rails flipper (~> 0.26.0) flipper-active_record (~> 0.26.0) diff --git a/rails/app/assets/stylesheets/application.sass.scss b/rails/app/assets/stylesheets/application.sass.scss index 1078c825e..9ad1cf4f0 100644 --- a/rails/app/assets/stylesheets/application.sass.scss +++ b/rails/app/assets/stylesheets/application.sass.scss @@ -3,6 +3,6 @@ @import "core/media"; @import "core/typography"; -@import "features/devise"; +@import "features/login"; @import "features/onboarding"; @import "features/welcome"; diff --git a/rails/app/assets/stylesheets/features/devise.scss b/rails/app/assets/stylesheets/features/login.scss similarity index 98% rename from rails/app/assets/stylesheets/features/devise.scss rename to rails/app/assets/stylesheets/features/login.scss index 5bbc1f3ab..01f643edf 100644 --- a/rails/app/assets/stylesheets/features/devise.scss +++ b/rails/app/assets/stylesheets/features/login.scss @@ -1,4 +1,4 @@ -.devise { +.login { background: url('welcome-bg.jpg') no-repeat center center fixed; background-size: cover; height: 100vh; @@ -121,7 +121,7 @@ display: none; } - .devise-card { + .login-card { margin-bottom: 0; } } diff --git a/rails/app/assets/stylesheets/features/onboarding.scss b/rails/app/assets/stylesheets/features/onboarding.scss index 73b5f6aaf..0a6dca411 100644 --- a/rails/app/assets/stylesheets/features/onboarding.scss +++ b/rails/app/assets/stylesheets/features/onboarding.scss @@ -130,10 +130,6 @@ .logo { display: none; } - - .devise-card { - margin-bottom: 0; - } } &-links { diff --git a/rails/app/constraints/role_routing_constraint.rb b/rails/app/constraints/role_routing_constraint.rb index a9f4b8fa3..943a8cb6f 100644 --- a/rails/app/constraints/role_routing_constraint.rb +++ b/rails/app/constraints/role_routing_constraint.rb @@ -9,6 +9,6 @@ def matches?(request) end def current_user(request) - User.find_by(id: request.session["warden.user.user.key"][0][0]) + User.find_by(id: request.session["user_id"]) end end \ No newline at end of file diff --git a/rails/app/controllers/api/base_controller.rb b/rails/app/controllers/api/base_controller.rb index fae5c11d8..6c039ffd1 100644 --- a/rails/app/controllers/api/base_controller.rb +++ b/rails/app/controllers/api/base_controller.rb @@ -1,7 +1,5 @@ module Api class BaseController < ActionController::Base - respond_to :json - rescue_from ActiveRecord::RecordNotFound, with: :not_found diff --git a/rails/app/controllers/application_controller.rb b/rails/app/controllers/application_controller.rb index e81278482..2c87dc3e6 100644 --- a/rails/app/controllers/application_controller.rb +++ b/rails/app/controllers/application_controller.rb @@ -1,4 +1,5 @@ class ApplicationController < ActionController::Base + include Sessions::Helpers include Pundit::Authorization include Locale diff --git a/rails/app/controllers/concerns/sessions/helpers.rb b/rails/app/controllers/concerns/sessions/helpers.rb new file mode 100644 index 000000000..9b0a57e27 --- /dev/null +++ b/rails/app/controllers/concerns/sessions/helpers.rb @@ -0,0 +1,27 @@ +module Sessions::Helpers + extend ActiveSupport::Concern + + included do + helper_method :current_user + helper_method :user_signed_in? + end + + def authenticate_user! + if !user_signed_in? + flash[:alert] = t("devise.failure.unauthenticated") + redirect_to login_path + end + end + + private + + def user_signed_in? + current_user.present? + end + + # Define the current_user method: + def current_user + # Look up the current user based on user_id in the session cookie: + @current_user ||= User.find(session[:user_id]) if session[:user_id] + end +end \ No newline at end of file diff --git a/rails/app/controllers/home_controller.rb b/rails/app/controllers/home_controller.rb index 337f4a096..8ffc44fc0 100644 --- a/rails/app/controllers/home_controller.rb +++ b/rails/app/controllers/home_controller.rb @@ -12,6 +12,7 @@ def index def community_search_index # This will eventually be a searchable action for the public maps and stories of # communities within this app. For now, it's just a placeholder. + render layout: "application" end def show diff --git a/rails/app/controllers/passwords_controller.rb b/rails/app/controllers/passwords_controller.rb index dae4b0a6e..e9469adaf 100644 --- a/rails/app/controllers/passwords_controller.rb +++ b/rails/app/controllers/passwords_controller.rb @@ -8,9 +8,14 @@ def edit def update @user = current_user if @user.update(password_params) - bypass_sign_in(@user) flash.notice = t("devise.passwords.updated_not_active") - redirect_to root_path + if @user.super_admin + redirect_to super_admin_root_path + elsif @user.community.present? + redirect_to member_root_path + else + redirect_to root_path + end else @user.password = nil @user.password_confirmation = nil diff --git a/rails/app/controllers/registrations_controller.rb b/rails/app/controllers/registrations_controller.rb deleted file mode 100644 index a3e96f8c8..000000000 --- a/rails/app/controllers/registrations_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -class RegistrationsController < Devise::RegistrationsController - - private - - def sign_up_params - params.require(:user).permit(:username, :name, :email, :role, :password, :password_confirmation) - end - - def account_update_params - params.require(:user).permit(:username, :name, :email, :role, :password, :password_confirmation, :current_password) - end - end \ No newline at end of file diff --git a/rails/app/controllers/sessions_controller.rb b/rails/app/controllers/sessions_controller.rb new file mode 100644 index 000000000..fdd87d8d7 --- /dev/null +++ b/rails/app/controllers/sessions_controller.rb @@ -0,0 +1,35 @@ +class SessionsController < ApplicationController + skip_before_action :authenticate_user! + + def new + # render login form + end + + # Create session + def create + user = User.where(username: login_params[:login]).or(User.where(email: login_params[:login])).first + + if session[:user_id] == user&.id + redirect_to root_path, notice: t("devise.failure.already_authenticated") + elsif user.authenticate(login_params[:password]) + session[:user_id] = user.id + user.update_tracked_fields(request) + redirect_to root_path, notice: t("devise.sessions.signed_in") + else + flash.now.alert = t("devise.failure.not_found_in_database", authentication_keys: "login") + render :new + end + end + + # Delete sessions + def destroy + session.delete(:user_id) + redirect_to login_path, notice: t("devise.sessions.signed_out") + end + + private + + def login_params + params.require(:user).permit(:login, :password) + end +end \ No newline at end of file diff --git a/rails/app/models/user.rb b/rails/app/models/user.rb index ffe270b67..96b4e18c7 100644 --- a/rails/app/models/user.rb +++ b/rails/app/models/user.rb @@ -1,15 +1,12 @@ class User < ApplicationRecord - # Include default devise modules. Others available are: :registerable, - # :recoverable, :confirmable, :lockable, :timeoutable and :omniauthable - devise :database_authenticatable, :rememberable, :trackable, :validatable + has_secure_password + attr_accessor :login belongs_to :community, optional: true, touch: true has_one_attached :photo validates :photo, content_type: [:png, :jpeg], size: { less_than_or_equal_to: 5.megabytes } - # Username is required for logging in with Devise. Email is optional. - # Remove email_required? override if username changes to optional. validates :username, uniqueness: true, presence: true, format: {without: /\s/, message: :invalid_username_format} enum role: { @@ -30,21 +27,24 @@ def display_name name.presence || username end - # Override Devise authentication to allow lookup via username or email - def self.find_first_by_auth_conditions(tainted_conditions) - if (login = tainted_conditions.delete(:login)) - where(username: login).or(where(email: login)).first - else - super - end + def update_tracked_fields(request) + old_current, new_current = self.current_sign_in_at, Time.now.utc + self.last_sign_in_at = old_current || new_current + self.current_sign_in_at = new_current + + old_current, new_current = self.current_sign_in_ip, request.remote_ip + self.last_sign_in_ip = old_current || new_current + self.current_sign_in_ip = new_current + + self.sign_in_count ||= 0 + self.sign_in_count += 1 end protected - # Override Devise Validatable: email is not required (username is) - def email_required? - false - end + # :password_digest is required for has_secure_password; + # mapped to :encrypted_password since that's what Devise used previously. + alias_attribute :password_digest, :encrypted_password end # == Schema Information diff --git a/rails/app/views/devise/mailer/confirmation_instructions.html.erb b/rails/app/views/devise/mailer/confirmation_instructions.html.erb deleted file mode 100644 index dc55f64f6..000000000 --- a/rails/app/views/devise/mailer/confirmation_instructions.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

Welcome <%= @email %>!

- -

You can confirm your account email through the link below:

- -

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/rails/app/views/devise/mailer/email_changed.html.erb b/rails/app/views/devise/mailer/email_changed.html.erb deleted file mode 100644 index 32f4ba803..000000000 --- a/rails/app/views/devise/mailer/email_changed.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Hello <%= @email %>!

- -<% if @resource.try(:unconfirmed_email?) %> -

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

-<% else %> -

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

-<% end %> diff --git a/rails/app/views/devise/mailer/password_change.html.erb b/rails/app/views/devise/mailer/password_change.html.erb deleted file mode 100644 index b41daf476..000000000 --- a/rails/app/views/devise/mailer/password_change.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Hello <%= @resource.email %>!

- -

We're contacting you to notify you that your password has been changed.

diff --git a/rails/app/views/devise/mailer/reset_password_instructions.html.erb b/rails/app/views/devise/mailer/reset_password_instructions.html.erb deleted file mode 100644 index f667dc12f..000000000 --- a/rails/app/views/devise/mailer/reset_password_instructions.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -

Hello <%= @resource.email %>!

- -

Someone has requested a link to change your password. You can do this through the link below.

- -

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

- -

If you didn't request this, please ignore this email.

-

Your password won't change until you access the link above and create a new one.

diff --git a/rails/app/views/devise/mailer/unlock_instructions.html.erb b/rails/app/views/devise/mailer/unlock_instructions.html.erb deleted file mode 100644 index 41e148bf2..000000000 --- a/rails/app/views/devise/mailer/unlock_instructions.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Hello <%= @resource.email %>!

- -

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

- -

Click the link below to unlock your account:

- -

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/rails/app/views/devise/registrations/edit.html.erb b/rails/app/views/devise/registrations/edit.html.erb deleted file mode 100644 index 216b0cab6..000000000 --- a/rails/app/views/devise/registrations/edit.html.erb +++ /dev/null @@ -1,57 +0,0 @@ -
-
-

<%= t("profile") %>

- - <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %> - -
- <%= f.label :username %> - <%= f.text_field :username, autofocus: true, autocomplete: "username" %> -
- -
- <%= f.label :name %> - <%= f.text_field :name, autocomplete: "name" %> -
- -
- <%= f.label :email %> - <%= f.email_field :email, autocomplete: "email" %> -
- - <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> -
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
- <% end %> - -
- <%= f.label :password %> (leave blank if you don't want to change it) - <%= f.password_field :password, autocomplete: "off" %> - <% if @minimum_password_length %> - - <%= @minimum_password_length %> characters minimum - <% end %> -
- -
- <%= f.label :password_confirmation %> - <%= f.password_field :password_confirmation, autocomplete: "off" %> -
- -
- <%= f.label :current_password %> (we need your current password to confirm your changes) - <%= f.password_field :current_password, autocomplete: "off" %> -
- -
- <%= f.submit "Update" %> -
- <% end %> - -

Cancel my account

- -

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %>

- - <%= link_to "Back", :back %> -
-
diff --git a/rails/app/views/devise/registrations/new.html.erb b/rails/app/views/devise/registrations/new.html.erb deleted file mode 100644 index 7457a3a86..000000000 --- a/rails/app/views/devise/registrations/new.html.erb +++ /dev/null @@ -1,43 +0,0 @@ -
-
-

Sign up

- - <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> - <%= render "devise/shared/error_messages", resource: resource %> - -
- <%= f.label :username %> - <%= f.text_field :username, autofocus: true, autocomplete: "username" %> -
- -
- <%= f.label :name %> - <%= f.text_field :name, autocomplete: "name" %> -
- -
- <%= f.label :email %> - <%= f.email_field :email, autocomplete: "email" %> -
- -
- <%= f.label :password %> - <% if @minimum_password_length %> - (<%= @minimum_password_length %> characters minimum) - <% end %> - <%= f.password_field :password, autocomplete: "off" %> -
- -
- <%= f.label :password_confirmation %> - <%= f.password_field :password_confirmation, autocomplete: "off" %> -
- -
- <%= f.submit "Sign up" %> -
- <% end %> - - <%= render "devise/shared/links" %> -
-
diff --git a/rails/app/views/devise/shared/_links.html.erb b/rails/app/views/devise/shared/_links.html.erb deleted file mode 100644 index 3b486c0f4..000000000 --- a/rails/app/views/devise/shared/_links.html.erb +++ /dev/null @@ -1,27 +0,0 @@ - diff --git a/rails/app/views/home/community_search_index.erb b/rails/app/views/home/community_search_index.erb index 86f22fa2f..9d39bf7c1 100644 --- a/rails/app/views/home/community_search_index.erb +++ b/rails/app/views/home/community_search_index.erb @@ -1,15 +1,16 @@
<%= image_tag 'logocombo.svg', alt: 'Terrastories' %> +

<%= t("community_search.title") %>

+

<%= t("community_search.description") %>

+ <%= link_to "https://explore.terrastories.app", class: "button" do %> + + <% end %>
-

<%= t("community_search.title") %>

-

<%= t("community_search.coming_soon") %>

-
<% if user_signed_in? %> - <%= link_to t("profile"), edit_registration_path(current_user) %> | - <%= link_to t("logout"), destroy_user_session_path, method: :delete, data: { turbo_method: :delete }, :class => 'navbar-link' %> + <%= link_to t("logout"), logout_path, method: :delete, data: { turbo_method: :delete }, :class => 'navbar-link' %> <% else %> - <%= link_to t("login"), new_user_session_path, :class => 'navbar-link' %> + <%= link_to t("login"), login_path, :class => 'navbar-link' %> <% end %>
diff --git a/rails/app/views/layouts/dashboard.html.erb b/rails/app/views/layouts/dashboard.html.erb index cc67fd622..19744e87a 100644 --- a/rails/app/views/layouts/dashboard.html.erb +++ b/rails/app/views/layouts/dashboard.html.erb @@ -90,7 +90,7 @@
<%= link_to t("dashboard.back_to_app"), home_map_path unless current_user.super_admin %> diff --git a/rails/app/views/devise/sessions/new.html.erb b/rails/app/views/sessions/new.html.erb similarity index 64% rename from rails/app/views/devise/sessions/new.html.erb rename to rails/app/views/sessions/new.html.erb index 46ce4d4cb..0a3ba43d1 100644 --- a/rails/app/views/devise/sessions/new.html.erb +++ b/rails/app/views/sessions/new.html.erb @@ -1,9 +1,9 @@ -
-
+