Authentification par Token en mode API et Basic en mode navigateur

This commit is contained in:
Jérémy Lecour 2022-01-25 23:31:08 +01:00 committed by Jérémy Lecour
parent 41d1d6d8fe
commit e13fb4828a
13 changed files with 179 additions and 203 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,5 @@
class ApplicationController < ActionController::Base
include Authentication
include BasicAuthentication
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

View file

@ -0,0 +1,3 @@
class AuthenticatedController < ApplicationController
before_action :authenticate_user!
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
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

View file

@ -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
def destroy
forget(current_user)
logout
redirect_to root_path, notice: "Signed out."
end
def new
end
end

View file

@ -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