From e13fb4828a0b9223c5f53198e5f6549fdb35d1a8 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 25 Jan 2022 23:31:08 +0100 Subject: [PATCH] Authentification par Token en mode API et Basic en mode navigateur --- app/controllers/api/v1/api_keys_controller.rb | 30 ------ app/controllers/api/v1/base_controller.rb | 5 + app/controllers/api/v1/checks_controller.rb | 17 ++-- app/controllers/application_controller.rb | 2 +- app/controllers/authenticated_controller.rb | 3 + app/controllers/checks_controller.rb | 2 +- .../concerns/api_key_authenticatable.rb | 31 ------- .../concerns/api_key_authentication.rb | 31 +++++++ app/controllers/concerns/authentication.rb | 58 ------------ .../concerns/basic_authentication.rb | 58 ++++++++++++ app/controllers/passwords_controller.rb | 93 +++++++++---------- app/controllers/sessions_controller.rb | 51 +++++----- config/routes.rb | 1 - 13 files changed, 179 insertions(+), 203 deletions(-) delete mode 100644 app/controllers/api/v1/api_keys_controller.rb create mode 100644 app/controllers/authenticated_controller.rb delete mode 100644 app/controllers/concerns/api_key_authenticatable.rb create mode 100644 app/controllers/concerns/api_key_authentication.rb delete mode 100644 app/controllers/concerns/authentication.rb create mode 100644 app/controllers/concerns/basic_authentication.rb diff --git a/app/controllers/api/v1/api_keys_controller.rb b/app/controllers/api/v1/api_keys_controller.rb deleted file mode 100644 index 51f8cd5..0000000 --- a/app/controllers/api/v1/api_keys_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -class Api::V1::ApiKeysController < Api::V1::BaseController - # include ApiKeyAuthenticable - - # # Require API key authentication - # prepend_before_action :authenticate_with_api_key!, only: %i[index destroy] - - def index - render json: current_bearer.api_keys - end - - def create - authenticate_with_http_basic do |email, password| - user = User.find_by email: email - - if user&.authenticate(password) - api_key = user.api_keys.create! token: SecureRandom.hex - - render json: api_key, status: :created and return - end - end - - render status: :unauthorized - end - - def destroy - api_key = current_bearer.api_keys.find(params[:id]) - - api_key.destroy - end - end \ No newline at end of file diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index b5b204b..344d851 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -1,4 +1,9 @@ class Api::V1::BaseController < ApplicationController + include ApiKeyAuthentication + + # Require API key authentication + prepend_before_action :authenticate_with_api_key! + # before_action :authenticate protect_from_forgery with: :null_session diff --git a/app/controllers/api/v1/checks_controller.rb b/app/controllers/api/v1/checks_controller.rb index be3091b..167d8a9 100644 --- a/app/controllers/api/v1/checks_controller.rb +++ b/app/controllers/api/v1/checks_controller.rb @@ -12,14 +12,15 @@ class Api::V1::ChecksController < Api::V1::BaseController end private - # Use callbacks to share common setup or constraints between actions. - def set_check - @check = Check.find(params[:id]) - end - # Only allow a list of trusted parameters through. - def check_params - params.require(:check).permit(:name, :description, :hostname) - end + # Use callbacks to share common setup or constraints between actions. + def set_check + @check = Check.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def check_params + params.require(:check).permit(:name, :description, :hostname) + end end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2842746..4f8ea28 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,5 @@ class ApplicationController < ActionController::Base - include Authentication + include BasicAuthentication include Pundit rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized diff --git a/app/controllers/authenticated_controller.rb b/app/controllers/authenticated_controller.rb new file mode 100644 index 0000000..a2f7666 --- /dev/null +++ b/app/controllers/authenticated_controller.rb @@ -0,0 +1,3 @@ +class AuthenticatedController < ApplicationController + before_action :authenticate_user! +end \ No newline at end of file diff --git a/app/controllers/checks_controller.rb b/app/controllers/checks_controller.rb index 62b1f0d..03cf6b4 100644 --- a/app/controllers/checks_controller.rb +++ b/app/controllers/checks_controller.rb @@ -1,4 +1,4 @@ -class ChecksController < ApplicationController +class ChecksController < AuthenticatedController before_action :set_check, only: %i[ show edit update destroy ] # GET /checks or /checks.json diff --git a/app/controllers/concerns/api_key_authenticatable.rb b/app/controllers/concerns/api_key_authenticatable.rb deleted file mode 100644 index 85f7939..0000000 --- a/app/controllers/concerns/api_key_authenticatable.rb +++ /dev/null @@ -1,31 +0,0 @@ -module ApiKeyAuthenticatable - include ActionController::HttpAuthentication::Basic::ControllerMethods - include ActionController::HttpAuthentication::Token::ControllerMethods - - extend ActiveSupport::Concern - - attr_reader :current_api_key - attr_reader :current_bearer - - # Use this to raise an error and automatically respond with a 401 HTTP status - # code when API key authentication fails - def authenticate_with_api_key! - @current_bearer = authenticate_or_request_with_http_token &method(:authenticator) - end - - # Use this for optional API key authentication - def authenticate_with_api_key - @current_bearer = authenticate_with_http_token &method(:authenticator) - end - - private - - attr_writer :current_api_key - attr_writer :current_bearer - - def authenticator(token, options) - @current_api_key = ApiKey.authenticate_by_token token - - current_api_key&.bearer - end - end \ No newline at end of file diff --git a/app/controllers/concerns/api_key_authentication.rb b/app/controllers/concerns/api_key_authentication.rb new file mode 100644 index 0000000..c40d0df --- /dev/null +++ b/app/controllers/concerns/api_key_authentication.rb @@ -0,0 +1,31 @@ +module ApiKeyAuthentication + include ActionController::HttpAuthentication::Basic::ControllerMethods + include ActionController::HttpAuthentication::Token::ControllerMethods + + extend ActiveSupport::Concern + + attr_reader :current_api_key + attr_reader :current_bearer + + # Use this to raise an error and automatically respond with a 401 HTTP status + # code when API key authentication fails + def authenticate_with_api_key! + @current_bearer = authenticate_or_request_with_http_token &method(:authenticator) + end + + # Use this for optional API key authentication + def authenticate_with_api_key + @current_bearer = authenticate_with_http_token &method(:authenticator) + end + + private + + attr_writer :current_api_key + attr_writer :current_bearer + + def authenticator(token, options) + @current_api_key = ApiKey.authenticate_by_token token + + current_api_key&.bearer + end +end \ No newline at end of file diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb deleted file mode 100644 index 7013c33..0000000 --- a/app/controllers/concerns/authentication.rb +++ /dev/null @@ -1,58 +0,0 @@ -module Authentication - extend ActiveSupport::Concern - - included do - before_action :current_user - helper_method :current_user - helper_method :user_signed_in? - end - - def authenticate_user! - store_location - redirect_to login_path, alert: "You need to login to access that page." unless user_signed_in? - end - - def login(user) - reset_session - user.regenerate_session_token - session[:current_user_session_token] = user.reload.session_token - end - - def forget(user) - cookies.delete :remember_token - user.regenerate_remember_token - end - - def logout - user = current_user - reset_session - user.regenerate_session_token - end - - def redirect_if_authenticated - redirect_to root_path, alert: "You are already logged in." if user_signed_in? - end - - def remember(user) - user.regenerate_remember_token - cookies.permanent.encrypted[:remember_token] = user.remember_token - end - - def store_location - session[:user_return_to] = request.original_url if request.get? && request.local? - end - - private - - def current_user - Current.user ||= if session[:current_user_session_token].present? - User.find_by(session_token: session[:current_user_session_token]) - elsif cookies.permanent.encrypted[:remember_token].present? - User.find_by(remember_token: cookies.permanent.encrypted[:remember_token]) - end - end - - def user_signed_in? - Current.user.present? - end - end diff --git a/app/controllers/concerns/basic_authentication.rb b/app/controllers/concerns/basic_authentication.rb new file mode 100644 index 0000000..35c21ac --- /dev/null +++ b/app/controllers/concerns/basic_authentication.rb @@ -0,0 +1,58 @@ +module BasicAuthentication + extend ActiveSupport::Concern + + included do + before_action :current_user + helper_method :current_user + helper_method :user_signed_in? + end + + def authenticate_user! + store_location + redirect_to login_path, alert: "You need to login to access that page." unless user_signed_in? + end + + def login(user) + reset_session + user.regenerate_session_token + session[:current_user_session_token] = user.reload.session_token + end + + def forget(user) + cookies.delete :remember_token + user.regenerate_remember_token + end + + def logout + user = current_user + reset_session + user.regenerate_session_token + end + + def redirect_if_authenticated + redirect_to root_path, alert: "You are already logged in." if user_signed_in? + end + + def remember(user) + user.regenerate_remember_token + cookies.permanent.encrypted[:remember_token] = user.remember_token + end + + def store_location + session[:user_return_to] = request.original_url if request.get? && request.local? + end + + private + + def current_user + Current.user ||= if session[:current_user_session_token].present? + User.find_by(session_token: session[:current_user_session_token]) + elsif cookies.permanent.encrypted[:remember_token].present? + User.find_by(remember_token: cookies.permanent.encrypted[:remember_token]) + end + end + + def user_signed_in? + Current.user.present? + end +end \ No newline at end of file diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 4545f7e..abb6576 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,53 +1,52 @@ class PasswordsController < ApplicationController - before_action :redirect_if_authenticated - - def create - @user = User.find_by(email: params[:user][:email].downcase) - if @user.present? - if @user.confirmed? - @user.send_password_reset_email! - redirect_to root_path, notice: "If that user exists we've sent instructions to their email." - else - redirect_to new_confirmation_path, alert: "Please confirm your email first." - end - else + before_action :redirect_if_authenticated + + def create + @user = User.find_by(email: params[:user][:email].downcase) + if @user.present? + if @user.confirmed? + @user.send_password_reset_email! redirect_to root_path, notice: "If that user exists we've sent instructions to their email." - end - end - - def edit - @user = User.find_signed(params[:password_reset_token], purpose: :reset_password) - if @user.present? && @user.unconfirmed? - redirect_to new_confirmation_path, alert: "You must confirm your email before you can sign in." - elsif @user.nil? - redirect_to new_password_path, alert: "Invalid or expired token." - end - end - - def new - end - - def update - @user = User.find_signed(params[:password_reset_token], purpose: :reset_password) - if @user - if @user.unconfirmed? - redirect_to new_confirmation_path, alert: "You must confirm your email before you can sign in." - elsif @user.update(password_params) - redirect_to login_path, notice: "Sign in." - else - flash.now[:alert] = @user.errors.full_messages.to_sentence - render :edit, status: :unprocessable_entity - end else - flash.now[:alert] = "Invalid or expired token." - render :new, status: :unprocessable_entity + redirect_to new_confirmation_path, alert: "Please confirm your email first." end - end - - private - - def password_params - params.require(:user).permit(:password, :password_confirmation) + else + redirect_to root_path, notice: "If that user exists we've sent instructions to their email." end end - \ No newline at end of file + + def edit + @user = User.find_signed(params[:password_reset_token], purpose: :reset_password) + if @user.present? && @user.unconfirmed? + redirect_to new_confirmation_path, alert: "You must confirm your email before you can sign in." + elsif @user.nil? + redirect_to new_password_path, alert: "Invalid or expired token." + end + end + + def new + end + + def update + @user = User.find_signed(params[:password_reset_token], purpose: :reset_password) + if @user + if @user.unconfirmed? + redirect_to new_confirmation_path, alert: "You must confirm your email before you can sign in." + elsif @user.update(password_params) + redirect_to login_path, notice: "Sign in." + else + flash.now[:alert] = @user.errors.full_messages.to_sentence + render :edit, status: :unprocessable_entity + end + else + flash.now[:alert] = "Invalid or expired token." + render :new, status: :unprocessable_entity + end + end + + private + + def password_params + params.require(:user).permit(:password, :password_confirmation) + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 650f5ea..59bd8ad 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,31 +1,30 @@ class SessionsController < ApplicationController - before_action :redirect_if_authenticated, only: [:create, :new] - before_action :authenticate_user!, only: [:destroy] - - def create - @user = User.authenticate_by(email: params[:user][:email].downcase, password: params[:user][:password]) - if @user - if @user.unconfirmed? - redirect_to new_confirmation_path, alert: "Incorrect email or password." - else - after_login_path = session[:user_return_to] || root_path - login @user - remember(@user) if params[:user][:remember_me] == "1" - redirect_to after_login_path, notice: "Signed in." - end + before_action :redirect_if_authenticated, only: [:create, :new] + before_action :authenticate_user!, only: [:destroy] + + def create + @user = User.authenticate_by(email: params[:user][:email].downcase, password: params[:user][:password]) + if @user + if @user.unconfirmed? + redirect_to new_confirmation_path, alert: "Incorrect email or password." else - flash.now[:alert] = "Incorrect email or password." - render :new, status: :unprocessable_entity + after_login_path = session[:user_return_to] || root_path + login @user + remember(@user) if params[:user][:remember_me] == "1" + redirect_to after_login_path, notice: "Signed in." end - end - - def destroy - forget(current_user) - logout - redirect_to root_path, notice: "Signed out." - end - - def new + else + flash.now[:alert] = "Incorrect email or password." + render :new, status: :unprocessable_entity end end - \ No newline at end of file + + def destroy + forget(current_user) + logout + redirect_to root_path, notice: "Signed out." + end + + def new + end +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 707624e..d4591f0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,7 +4,6 @@ Rails.application.routes.draw do namespace :v1 do defaults format: :json do get '/ping', to: 'base#ping' - resources :api_keys, path: 'api-keys', only: %i[index create destroy] resources :checks, only: [:create] end end