diff --git a/Gemfile b/Gemfile index 7bb22c1..6879503 100644 --- a/Gemfile +++ b/Gemfile @@ -39,6 +39,9 @@ gem 'net-smtp', require: false gem 'net-imap', require: false gem 'net-pop', require: false gem "apipie-rails", "~> 1.2.0" +gem 'omniauth', '>=2.0.0' +gem 'omniauth-rails_csrf_protection' +gem 'omniauth-tara', github: 'internetee/omniauth-tara' # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' diff --git a/app/controllers/auth/tara_controller.rb b/app/controllers/auth/tara_controller.rb new file mode 100644 index 0000000..a9d83fa --- /dev/null +++ b/app/controllers/auth/tara_controller.rb @@ -0,0 +1,51 @@ +# rubocop:disable Metrics + +module Auth + class TaraController < ParentController + allow_unauthenticated + + def callback + expires_now + + unless in_white_list? + flash[:alert] = I18n.t('.access_denied') + redirect_to sign_in_path, status: :see_other and return + end + + session[:omniauth_hash] = user_hash.delete_if { |key, _| key == 'credentials' } + @user = User.from_omniauth(user_hash) + @user.save! && @user.reload + + @app_session = create_app_session + + if @app_session + log_in @app_session + set_current_session + + redirect_to root_path, status: :see_other + else + flash[:alert] = I18n.t('.incorrect_details') + render 'dashboard/index', status: :unprocessable_entity + end + end + + private + + def in_white_list? + WhiteCode.find_by(code: user_hash['uid'].slice(2..-1)).present? + end + + def set_current_session + Current.user = @user + flash[:notice] = I18n.t('.success') + end + + def user_hash + request.env['omniauth.auth'] + end + + def create_app_session + @user.app_sessions.create + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/authenticate.rb b/app/controllers/concerns/authenticate.rb new file mode 100644 index 0000000..440c000 --- /dev/null +++ b/app/controllers/concerns/authenticate.rb @@ -0,0 +1,72 @@ +module Authenticate + extend ActiveSupport::Concern + + included do + before_action :authenticate + before_action :need_to_login, unless: :logged_in? + + helper_method :logged_in? + helper_method :current_user + end + + class_methods do + def skip_authentication(**options) + skip_before_action :authenticate, options + skip_before_action :need_to_login, options + end + + def allow_unauthenticated(**options) + skip_before_action :need_to_login, options + end + end + + protected + + def log_in(app_session, remember_me: false) + if remember_me + cookies.encrypted.permanent[:app_session] = { + value: app_session.to_h + } + else + cookies.signed[:app_session] = { + value: app_session.to_h, + expires: 1.day + } + end + end + + def logout + Current&.app_session&.destroy + end + + def logged_in? + Current.user.present? + end + + def current_user + Current.user + end + + private + + def need_to_login + flash[:notice] = t('login_required') + render 'sessions/new', status: :unauthorized + end + + def authenticate + cookie = cookies.encrypted[:app_session]&.with_indifferent_access + cookie = cookies.signed[:app_session]&.with_indifferent_access if cookie.nil? + + return nil if cookie.nil? + + user = User.find(cookie[:user_id]) + app_session = user&.authenticate_session_token(cookie[:app_session], cookie[:token]) + + Current.user = app_session&.user + Current.app_session = app_session + rescue NoMatchingPatternError, ActiveRecord::RecordNotFound + Current.user = nil + Current.app_session = nil + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 37e6dff..5e5dd00 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,6 +1,4 @@ class DashboardController < ParentController - before_action :require_user_logged_in! - def index @pagy, @invoices = pagy(Invoice.search(params), items: params[:per_page] ||= 25, diff --git a/app/controllers/dashboards/invoice_status_controller.rb b/app/controllers/dashboards/invoice_status_controller.rb index c3b231b..6d27140 100644 --- a/app/controllers/dashboards/invoice_status_controller.rb +++ b/app/controllers/dashboards/invoice_status_controller.rb @@ -1,6 +1,4 @@ class Dashboards::InvoiceStatusController < ParentController - before_action :require_user_logged_in! - def update @invoice = Invoice.find(params[:id]) temporary_unavailable and return unless @invoice.registry? diff --git a/app/controllers/everypay_controller.rb b/app/controllers/everypay_controller.rb index 4ea74e8..8578be0 100644 --- a/app/controllers/everypay_controller.rb +++ b/app/controllers/everypay_controller.rb @@ -1,6 +1,4 @@ class EverypayController < ParentController - before_action :require_user_logged_in! - def index; end def everypay_data diff --git a/app/controllers/invoice_details/descriptions_controller.rb b/app/controllers/invoice_details/descriptions_controller.rb index a8c0650..f2f9b7e 100644 --- a/app/controllers/invoice_details/descriptions_controller.rb +++ b/app/controllers/invoice_details/descriptions_controller.rb @@ -1,6 +1,4 @@ class InvoiceDetails::DescriptionsController < ParentController - before_action :require_user_logged_in! - def show @description = Invoice.find(params[:id])&.description end diff --git a/app/controllers/invoice_details/directo_controller.rb b/app/controllers/invoice_details/directo_controller.rb index 9f2e588..572d4c7 100644 --- a/app/controllers/invoice_details/directo_controller.rb +++ b/app/controllers/invoice_details/directo_controller.rb @@ -1,8 +1,5 @@ class InvoiceDetails::DirectoController < ParentController require 'rexml/document' - - before_action :require_user_logged_in! - def show directo = Invoice.find(params[:id]) diff --git a/app/controllers/invoice_details/everypay_response_controller.rb b/app/controllers/invoice_details/everypay_response_controller.rb index 5ecd775..031f4df 100644 --- a/app/controllers/invoice_details/everypay_response_controller.rb +++ b/app/controllers/invoice_details/everypay_response_controller.rb @@ -1,6 +1,4 @@ class InvoiceDetails::EverypayResponseController < ParentController - before_action :require_user_logged_in! - def show everypay = Invoice.find(params[:id]) @everypay = everypay.everypay_response diff --git a/app/controllers/invoice_details/payment_references_controller.rb b/app/controllers/invoice_details/payment_references_controller.rb index 9390352..9840d13 100644 --- a/app/controllers/invoice_details/payment_references_controller.rb +++ b/app/controllers/invoice_details/payment_references_controller.rb @@ -1,6 +1,4 @@ class InvoiceDetails::PaymentReferencesController < ParentController - before_action :require_user_logged_in! - def show @payment_reference = Invoice.find(params[:id])&.payment_reference end diff --git a/app/controllers/parent_controller.rb b/app/controllers/parent_controller.rb index 73e2d52..78e54df 100644 --- a/app/controllers/parent_controller.rb +++ b/app/controllers/parent_controller.rb @@ -3,25 +3,14 @@ class ParentController < ActionController::Base include AbstractController::Rendering include ActionView::Layouts - # append_view_path "#{Rails.root}/app/views/layouts" - layout "application" + include Authenticate + + layout 'application' skip_before_action :verify_authenticity_token helper_method :turbo_frame_request? - before_action :set_current_user - def render_turbo_flash turbo_stream.update('flash', partial: 'shared/flash') end - - def set_current_user - # finds user with session data and stores it if present - Current.user = User.find_by(id: session[:user_id]) if session[:user_id] - end - - def require_user_logged_in! - # allows only logged in user - redirect_to sign_in_path, alert: 'You must be signed in', turbolinks: false if Current.user.nil? - end end diff --git a/app/controllers/references_controller.rb b/app/controllers/references_controller.rb index d85cd72..196b39f 100644 --- a/app/controllers/references_controller.rb +++ b/app/controllers/references_controller.rb @@ -1,6 +1,4 @@ class ReferencesController < ParentController - before_action :require_user_logged_in! - def index @pagy, @references = pagy(Reference.search(params), items: params[:per_page] ||= 25, link_extra: 'data-turbo-action="advance"') diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb deleted file mode 100644 index b104d73..0000000 --- a/app/controllers/registrations_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -class RegistrationsController < ParentController - before_action :require_user_logged_in! - - def new - @user = User.new - end - - def create - @user = User.new(user_params) - @new_user = User.new - - if @user.save - flash[:notice] = 'Successfully created account' - - respond_to do |format| - format.turbo_stream do - render turbo_stream: [ - turbo_stream.prepend('users', partial: 'users/user', locals: { user: @user }), - turbo_stream.update('new_user', partial: 'users/form', - locals: { user: @new_user, url: sign_up_path }), - ] - end - end - else - render :new - end - end - - private - - def user_params - params.require(:user).permit(:email, :password, :password_confirmation) - end -end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 3d3d511..b20c907 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,19 +1,23 @@ class SessionsController < ParentController + allow_unauthenticated + def new; end - def create - user = User.find_by(email: params[:email]) - if user.present? && user.authenticate(params[:password]) - session[:user_id] = user.id - redirect_to root_path, status: :see_other, flash: { notice: 'Logged in successfully' } - else - flash.now[:alert] = 'Wrong username/password' - render :new, status: :unprocessable_entity - end - end + # def create + # user = User.find_by(email: params[:email]) + # if user.present? && user.authenticate(params[:password]) + # session[:user_id] = user.id + # redirect_to root_path, status: :see_other, flash: { notice: 'Logged in successfully' } + # else + # flash.now[:alert] = 'Wrong username/password' + # render :new, status: :unprocessable_entity + # end + # end def destroy - session[:user_id] = nil - redirect_to sign_in_path, flash: { notice: 'Logged out' } + logout + + flash[:success] = t('.success') + redirect_to root_path, status: :see_other end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb deleted file mode 100644 index 286bf63..0000000 --- a/app/controllers/users_controller.rb +++ /dev/null @@ -1,84 +0,0 @@ -class UsersController < ParentController - before_action :require_user_logged_in! - - before_action :set_admin, only: %i[edit update destroy] - - def index - @users = User.all - - @new_user = User.new - end - - def new; end - - def edit - return if @user != Current.user - end - - def update - respond_to do |format| - if Current.user.update(strong_params) - flash.now[:notice] = 'Admin user was updated' - format.turbo_stream do - render turbo_stream: [ - render_turbo_flash, - turbo_stream.replace(Current.user, - partial: 'users/user', - locals: { user: Current.user }), - ] - end - else - format.turbo_stream do - flash.now[:alert] = 'Something went wrong!' - render turbo_stream: [ - render_turbo_flash, - ] - end - end - end - end - - def destroy - title = @user.email - respond_to do |format| - if @user.destroy - flash.now[:alert] = "#{title} was deleted" - format.turbo_stream do - render turbo_stream: [ - turbo_stream.remove(@user), - ] - end - else - format.turbo_stream do - flash.now[:alert] = 'Something went wrong!' - render turbo_stream: [] - end - end - end - end - - def search - redirect_to users_path and return unless params[:title_search].present? - - @search_param = params[:title_search].to_s.downcase - @users = User.search_by_email(@search_param).with_pg_search_highlight - - respond_to do |format| - format.turbo_stream do - render turbo_stream: [ - turbo_stream.update('users', partial: 'users/users', locals: { users: @users }), - ] - end - end - end - - private - - def strong_params - params.require(:user).permit(:email, :password, :password_confirmation) - end - - def set_admin - @user = User.find(params[:id]) - end -end diff --git a/app/controllers/white_codes_controller.rb b/app/controllers/white_codes_controller.rb new file mode 100644 index 0000000..9b931b7 --- /dev/null +++ b/app/controllers/white_codes_controller.rb @@ -0,0 +1,93 @@ +# rubocop:disable Metrics +class WhiteCodesController < ParentController + before_action :set_white_code, only: %i[edit update destroy] + + def index + @white_codes = WhiteCode.all + @new_white_code = WhiteCode.new + end + + def new + @white_code = WhiteCode.new + end + + def edit; end + + def create + @white_code = WhiteCode.new(strong_params) + + respond_to do |format| + if @white_code.save + flash.now[:notice] = 'Identity code added to white list' + @white_code_new = WhiteCode.new + + format.turbo_stream do + render turbo_stream: [ + render_turbo_flash, + turbo_stream.append('white_codes', partial: 'white_codes/white_code', locals: { white_code: @white_code }), + turbo_stream.replace('admin_form', partial: 'white_codes/form', locals: { white_code: @white_code_new, url: white_codes_path }) + ] + end + else + format.turbo_stream do + flash.now[:alert] = @white_code.errors.full_messages.join(', ') + render turbo_stream: [render_turbo_flash] + end + end + end + end + + def update + respond_to do |format| + if @white_code.update(strong_params) + flash.now[:notice] = 'Identity code in white list was updated' + @white_code_new = WhiteCode.new + + format.turbo_stream do + render turbo_stream: [ + render_turbo_flash, + turbo_stream.replace(@white_code, partial: 'white_codes/white_code', locals: { white_code: @white_code }), + turbo_stream.replace('admin_form', partial: 'white_codes/form', locals: { white_code: @white_code_new, url: white_codes_path }) + ] + end + else + format.turbo_stream do + flash.now[:alert] = @white_code.errors.full_messages.join(', ') + render turbo_stream: [render_turbo_flash] + end + end + end + end + + def destroy + code = @white_code.code + respond_to do |format| + if @white_code.destroy + flash.now[:alert] = "#{code} was deleted from white list" + format.turbo_stream do + render turbo_stream: [ + render_turbo_flash, + turbo_stream.remove(@white_code) + ] + end + else + format.turbo_stream do + flash.now[:alert] = @white_code.errors.full_messages.join(', ') + render turbo_stream: [ + render_turbo_flash + ] + end + end + end + end + + private + + def strong_params + params.require(:white_code).permit(:code) + end + + def set_white_code + @white_code = WhiteCode.find(params[:id]) + end +end diff --git a/app/models/app_session.rb b/app/models/app_session.rb new file mode 100644 index 0000000..08ac617 --- /dev/null +++ b/app/models/app_session.rb @@ -0,0 +1,17 @@ +class AppSession < ApplicationRecord + belongs_to :user + + has_secure_password :token, validations: false + + before_create do + self.token = self.class.generate_unique_secure_token + end + + def to_h + { + user_id: user.id, + app_session: id, + token: + } + end +end diff --git a/app/models/current.rb b/app/models/current.rb index 61ca520..df3e894 100644 --- a/app/models/current.rb +++ b/app/models/current.rb @@ -1,4 +1,3 @@ class Current < ActiveSupport::CurrentAttributes - # makes Current.user accessible in view files. - attribute :user + attribute :user, :app_session end diff --git a/app/models/user.rb b/app/models/user.rb index c5a5921..b7caf81 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,10 +1,11 @@ class User < ApplicationRecord include PgSearch::Model + include Authentication - has_secure_password + has_secure_password :password, validations: false - validates :email, presence: true, uniqueness: true, - format: { with: /\A[^@\s]+@[^@\s]+\z/, message: 'Invalid email' } + validates :email, format: { with: /\A[^@\s]+@[^@\s]+\z/, message: 'Invalid email' }, allow_nil: true + has_many :app_sessions, dependent: :destroy pg_search_scope :search_by_email, against: [:email], @@ -13,4 +14,16 @@ class User < ApplicationRecord prefix: true } } + + def self.from_omniauth(omniauth_hash) + uid = omniauth_hash['uid'] + provider = omniauth_hash['provider'] + + user = User.find_or_initialize_by(identity_code: uid.slice(2..-1)) + user.provider = provider + user.uid = uid + user.identity_code = uid.slice(2..-1) + + user + end end diff --git a/app/models/user/authentication.rb b/app/models/user/authentication.rb new file mode 100644 index 0000000..c1e6602 --- /dev/null +++ b/app/models/user/authentication.rb @@ -0,0 +1,9 @@ +module User::Authentication + extend ActiveSupport::Concern + + def authenticate_session_token(app_session_id, token) + app_sessions.find(app_session_id).authenticate_token(token) + rescue ActiveRecord::RecordNotFound + nil + end +end diff --git a/app/models/white_code.rb b/app/models/white_code.rb new file mode 100644 index 0000000..91a5b1a --- /dev/null +++ b/app/models/white_code.rb @@ -0,0 +1,3 @@ +class WhiteCode < ApplicationRecord + validates :code, presence: true, uniqueness: true, length: { is: 11 } +end diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index 377cf62..ecde9a3 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -4,18 +4,7 @@

Sign In

EIS

- <%= form_with url: sign_in_path do |f| %> -
- <%= f.label 'email:'%>
- <%= f.text_field :email, id: 'email', class: "w-full px-3 py-2 mt-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" %> -
-
- <%= f.label 'password:'%>
- <%= f.password_field :password, id: 'password', class: "w-full px-3 py-2 mt-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" %> -
-
- <%= f.submit 'Log In', class: 'w-32 mt-4 h-10 py-2 px-4 border border-transparent text-sm font-semibold rounded-md text-white bg-violet-700 hover:bg-violet-800 transition duration-150 ease-in-out shadow-md cursor-pointer mr-6' %> -
- <% end %> + + <%= button_to t('.login_with_eeid'), "/auth/tara", data: { turbo: false }, class: "text-black inline-flex w-full justify-center rounded-md border border-gray-300 bg-white py-2 px-4 text-sm font-medium text-gray-500 shadow-sm hover:bg-gray-50 mt-6" %> - + diff --git a/app/views/shared/_navbar.html.erb b/app/views/shared/_navbar.html.erb index 18da27f..19130f5 100644 --- a/app/views/shared/_navbar.html.erb +++ b/app/views/shared/_navbar.html.erb @@ -22,8 +22,8 @@ Get data from Everypay <% end %> - <%= link_to users_path, class: 'px-6 py-1 flex flex-col md:flex-row md:items-center' do %> - Admins + <%= link_to white_codes_path, class: 'px-6 py-1 flex flex-col md:flex-row md:items-center' do %> + White list <% end %> <%= link_to logout_path, method: :destroy, data: { "turbo-method": 'delete' }, class: 'px-6 py-1 flex flex-col md:flex-row md:items-center' do %> diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb deleted file mode 100644 index 729f930..0000000 --- a/app/views/users/_form.html.erb +++ /dev/null @@ -1,37 +0,0 @@ - -

Create Admin

-<%= form_with model: user, url: url do |f| %> -
- <%= f.label 'email', class: "text-sm font-bold" %>
- <%= f.text_field :email, autofocus: true, required: true, class: "w-full px-3 py-2 mt-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm", placeholder: "" %> -
- - <% unless f.object.new_record? %> -
-
-
- -
- - Leave this field empty if you won't to update password - -
-
-
-
- <% end %> - -
- <%= f.label 'password', class: "text-sm font-bold" %>
- <%= f.password_field :password, autofocus: true, required: f.object.new_record? ? true : false, class: "w-full px-3 py-2 mt-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm", placeholder: "" %> -
-
- <%= f.label 'password_confirmation', class: "text-sm font-bold" %>
- <%= f.password_field :password_confirmation, autofocus: true, required: f.object.new_record? ? true : false, class: "w-full px-3 py-2 mt-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm", placeholder: "" %> -
-
- <%= f.submit f.object.new_record? ? '+ Create new admin user' : 'Update admin user', class: "h-10 py-2 mt-4 px-4 border border-transparent text-sm font-semibold rounded-md text-white bg-violet-700 hover:bg-violet-800 transition duration-150 ease-in-out shadow-md cursor-pointer" %> -
-<% end %> diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb deleted file mode 100644 index a5ba946..0000000 --- a/app/views/users/_user.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<%= content_tag :tr, id: dom_id(user), class: 'bg-white' do %> - <%= user.email %> - - - <%= link_to edit_user_path(user), class: 'w-6 h-6 p-2 rounded-full bg-blue-300 hover:bg-blue-500 text-black border-gray-300 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black', data: { turbo_frame: 'admin_form' } do %> - - <% end %> - - <%= button_to user, method: :delete, - class: "w-6 h-6 p-2 ml-4 rounded-full bg-red-300 hover:bg-red-500 text-black border-gray-300 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black" do %> - - <% end %> - -<% end %> diff --git a/app/views/users/_users.html.erb b/app/views/users/_users.html.erb deleted file mode 100644 index b3f92f7..0000000 --- a/app/views/users/_users.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render users %> \ No newline at end of file diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb deleted file mode 100644 index 7c2a30f..0000000 --- a/app/views/users/edit.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= turbo_frame_tag 'admin_form' do %> - <%= render partial: 'users/form', locals: { user: @user, url: user_path } %> -<% end %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb deleted file mode 100644 index 1cb986b..0000000 --- a/app/views/users/index.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -
- <%= form_with url: search_users_path, - method: :post, - data: { controller: 'debounce', debounce_target: 'form' } do |form| %> - <%= form.search_field :title_search, value: params[:title_search], oninput: "this.form.requestSubmit()", - class: 'w-full pl-3 pr-10 py-2 border-2 border-gray-200 rounded-xl hover:border-gray-300 focus:outline-none focus:border-blue-500 transition-colors mb-7 bg-white', - placeholder: 'Search by email' %> - <% end %> - -
-
- - - - - - - - - <%= render partial: 'users', locals: { users: @users } %> - -
Email - <%= button_to 'New admin user', '#', class: 'h-10 py-2 my-1 px-4 border border-transparent text-sm font-semibold rounded-md text-white bg-violet-700 hover:bg-violet-800 transition duration-150 ease-in-out shadow-md cursor-pointer', data: { turbo_frame: 'admin_form' } %> -
-
- -
- <%= turbo_frame_tag 'admin_form' do %> - <%= render partial: 'form', locals: { user: @new_user, url: sign_up_path } %> - <% end %> -
-
-
\ No newline at end of file diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb deleted file mode 100644 index d88c924..0000000 --- a/app/views/users/new.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= turbo_frame_tag 'admin_form' do %> - <%= render partial: 'users/form', locals: { user: User.new, url: new_user_path } %> -<% end %> diff --git a/app/views/white_codes/_form.html.erb b/app/views/white_codes/_form.html.erb new file mode 100644 index 0000000..b2fb58b --- /dev/null +++ b/app/views/white_codes/_form.html.erb @@ -0,0 +1,14 @@ + +<%= turbo_frame_tag 'admin_form' do %> +

Create Admin

+ <%= form_with model: white_code, url: url do |f| %> +
+ <%= f.label 'code', class: "text-sm font-bold" %>
+ <%= f.text_field :code, autofocus: true, required: true, class: "w-full px-3 py-2 mt-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm", placeholder: "" %> +
+ +
+ <%= f.submit f.object.new_record? ? '+ Add identity code to white list' : 'Update white code', class: "h-10 py-2 mt-4 px-4 border border-transparent text-sm font-semibold rounded-md text-white bg-violet-700 hover:bg-violet-800 transition duration-150 ease-in-out shadow-md cursor-pointer" %> +
+ <% end %> +<% end %> diff --git a/app/views/white_codes/_white_code.html.erb b/app/views/white_codes/_white_code.html.erb new file mode 100644 index 0000000..7857a66 --- /dev/null +++ b/app/views/white_codes/_white_code.html.erb @@ -0,0 +1,14 @@ +<%= content_tag :tr, id: dom_id(white_code), class: 'bg-white' do %> + <%= white_code.code %> + + + <%= link_to edit_white_code_path(white_code), class: 'w-6 h-6 p-2 rounded-full bg-blue-300 hover:bg-blue-500 text-black border-gray-300 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black', data: { turbo_frame: 'admin_form' } do %> + + <% end %> + + <%= button_to white_code, method: :delete, + class: "w-6 h-6 p-2 ml-4 rounded-full bg-red-300 hover:bg-red-500 text-black border-gray-300 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black", form: { data: { turbo_confirm: t(".confirm_delete") } } do %> + + <% end %> + +<% end %> diff --git a/app/views/white_codes/edit.html.erb b/app/views/white_codes/edit.html.erb new file mode 100644 index 0000000..ec289a9 --- /dev/null +++ b/app/views/white_codes/edit.html.erb @@ -0,0 +1,3 @@ +<%= turbo_frame_tag 'admin_form' do %> + <%= render partial: 'white_codes/form', locals: { white_code: @white_code, url: white_code_path } %> +<% end %> diff --git a/app/views/white_codes/index.html.erb b/app/views/white_codes/index.html.erb new file mode 100644 index 0000000..e8c8c54 --- /dev/null +++ b/app/views/white_codes/index.html.erb @@ -0,0 +1,25 @@ +
+
+
+ + + + + + + + + <%= render @white_codes %> + +
Code + <%= link_to 'New identity code to white list', new_white_code_path, class: 'h-10 py-2 my-1 px-4 border border-transparent text-sm font-semibold rounded-md text-white bg-violet-700 hover:bg-violet-800 transition duration-150 ease-in-out shadow-md cursor-pointer', data: { turbo_frame: 'admin_form' } %> +
+
+ +
+ <%= turbo_frame_tag 'admin_form' do %> + <%= render partial: 'form', locals: { white_code: @new_white_code, url: white_codes_path } %> + <% end %> +
+
+
\ No newline at end of file diff --git a/app/views/white_codes/new.html.erb b/app/views/white_codes/new.html.erb new file mode 100644 index 0000000..11f5427 --- /dev/null +++ b/app/views/white_codes/new.html.erb @@ -0,0 +1,3 @@ +<%= turbo_frame_tag 'admin_form' do %> + <%= render partial: 'white_codes/form', locals: { white_code: @white_code, url: new_white_code_path } %> +<% end %> diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000..b008508 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,45 @@ +OpenIDConnect.logger = Rails.logger +OpenIDConnect.debug! + +OmniAuth.config.on_failure = proc do |env| + OmniAuth::FailureEndpoint.new(env).redirect_to_failure +end + +OmniAuth.config.logger = Rails.logger +OmniAuth.config.allowed_request_methods = %i[post] + +tara_keys = ENV['tara_keys'] +tara_scope = %w[openid idcard mid smartid] +tara_issuer = ENV['tara_issuer'] +tara_host = ENV['tara_host'] +tara_jwks_uri = ENV['tara_jwks_uri'] +tara_identifier = ENV['tara_identifier'] +tara_secret = ENV['tara_secret'] +tara_redirect_uri = ENV['tara_redirect_uri'] + +Rails.application.config.middleware.use OmniAuth::Builder do + provider 'tara', { + name: 'tara', + scope: tara_scope, + state: SecureRandom.hex(10), + client_signing_alg: :RS256, + client_jwk_signing_key: tara_keys, + send_scope_to_token_endpoint: false, + send_nonce: true, + issuer: tara_issuer, + discovery: true, + + client_options: { + scheme: 'https', + host: tara_host, + port: nil, + authorization_endpoint: '/auth/:provider', + token_endpoint: '/auth/token', + userinfo_endpoint: nil, # Not implemented + jwks_uri: tara_jwks_uri, + identifier: tara_identifier, + secret: tara_secret, + redirect_uri: tara_redirect_uri, + } + } +end diff --git a/config/routes.rb b/config/routes.rb index e7e79a8..e8a4ef3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,17 +2,9 @@ apipie root 'dashboard#index' - get 'sign_up', to: 'registrations#new' - post 'sign_up', to: 'registrations#create' get 'sign_in', to: 'sessions#new' - post 'sign_in', to: 'sessions#create', as: 'log_in' delete 'logout', to: 'sessions#destroy' - - resources :users, only: [:index, :destroy, :edit, :update, :new] do - collection do - post :search - end - end + resources :white_codes resources :dashboard do collection do @@ -39,6 +31,13 @@ end resources :references, only: [:index] + + namespace 'auth' do + resources :invitations, only: %i[show edit create update destroy] + match '/:provider/callback', via: %i[get post], to: 'tara#callback', as: :tara_callback + match '/tara/cancel', via: %i[get post delete], to: 'tara#cancel', as: :tara_cancel + get '/failure', to: 'tara#cancel' + end namespace :api, defaults: { format: :json } do namespace :v1 do diff --git a/db/migrate/20230920121119_create_app_sessions.rb b/db/migrate/20230920121119_create_app_sessions.rb new file mode 100644 index 0000000..7830f47 --- /dev/null +++ b/db/migrate/20230920121119_create_app_sessions.rb @@ -0,0 +1,10 @@ +class CreateAppSessions < ActiveRecord::Migration[7.0] + def change + create_table :app_sessions do |t| + t.references :user, null: false, foreign_key: true + t.string :token_digest + + t.timestamps + end + end +end diff --git a/db/migrate/20230920122819_add_tara_attributes_to_user.rb b/db/migrate/20230920122819_add_tara_attributes_to_user.rb new file mode 100644 index 0000000..1ec0617 --- /dev/null +++ b/db/migrate/20230920122819_add_tara_attributes_to_user.rb @@ -0,0 +1,7 @@ +class AddTaraAttributesToUser < ActiveRecord::Migration[7.0] + def change + add_column :users, :identity_code, :string + add_column :users, :uid, :string, null: false, default: '' + add_column :users, :provider, :string, null: false, default: '' + end +end diff --git a/db/migrate/20230921075925_create_white_codes.rb b/db/migrate/20230921075925_create_white_codes.rb new file mode 100644 index 0000000..d77b56a --- /dev/null +++ b/db/migrate/20230921075925_create_white_codes.rb @@ -0,0 +1,9 @@ +class CreateWhiteCodes < ActiveRecord::Migration[7.0] + def change + create_table :white_codes do |t| + t.string :code, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b7c75bf..3b96e01 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_02_14_083243) do +ActiveRecord::Schema[7.0].define(version: 2023_09_21_075925) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -42,6 +42,14 @@ t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end + create_table "app_sessions", force: :cascade do |t| + t.bigint "user_id", null: false + t.string "token_digest" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_app_sessions_on_user_id" + end + create_table "invoices", force: :cascade do |t| t.integer "invoice_number", null: false t.string "initiator", null: false @@ -85,8 +93,18 @@ t.string "password_digest" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "identity_code" + t.string "uid", default: "", null: false + t.string "provider", default: "", null: false + end + + create_table "white_codes", force: :cascade do |t| + t.string "code", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" + add_foreign_key "app_sessions", "users" end diff --git a/lib/tasks/add_code_to_whitelist.rake b/lib/tasks/add_code_to_whitelist.rake new file mode 100644 index 0000000..c25d942 --- /dev/null +++ b/lib/tasks/add_code_to_whitelist.rake @@ -0,0 +1,7 @@ +namespace :whitelist do + + # rake whitelist:add_code[60001019906] + task :add_code, [:code] => [:environment] do |_t, args| + WhiteCode.create!(code: args[:code]) + end +end diff --git a/spec/factories/app_sessions.rb b/spec/factories/app_sessions.rb new file mode 100644 index 0000000..ef235eb --- /dev/null +++ b/spec/factories/app_sessions.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :app_session do + + end +end diff --git a/spec/factories/white_codes.rb b/spec/factories/white_codes.rb new file mode 100644 index 0000000..a4f6aa3 --- /dev/null +++ b/spec/factories/white_codes.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :white_code do + code { "MyString" } + end +end diff --git a/spec/models/app_session_spec.rb b/spec/models/app_session_spec.rb new file mode 100644 index 0000000..9baba27 --- /dev/null +++ b/spec/models/app_session_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe AppSession, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/white_code_spec.rb b/spec/models/white_code_spec.rb new file mode 100644 index 0000000..45d5eb5 --- /dev/null +++ b/spec/models/white_code_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe WhiteCode, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end