From c1d173d601504f577e4eb1c2061c8b4ff3f51112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Turbelin?= Date: Tue, 28 Apr 2020 12:12:04 +0200 Subject: [PATCH] Add the access token logic, light version --- Gemfile.lock | 12 +-- .../dfc_provider/api/products_controller.rb | 59 +++++++++++-- .../dfc_provider/authorization_control.rb | 31 +++++++ engines/dfc_provider/dfc_provider.gemspec | 20 +++-- .../api/products_controller_spec.rb | 82 ++++++++++++++++--- engines/dfc_provider/spec/spec_helper.rb | 4 +- 6 files changed, 175 insertions(+), 33 deletions(-) create mode 100644 engines/dfc_provider/app/services/dfc_provider/authorization_control.rb diff --git a/Gemfile.lock b/Gemfile.lock index 4dc1791427..cf7b88e370 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,16 +56,18 @@ GIT rails-i18n spree_core (>= 1.1) -PATH - remote: engines/dfc_provider - specs: - dfc_provider (0.0.1) - PATH remote: engines/catalog specs: catalog (0.0.1) +PATH + remote: engines/dfc_provider + specs: + dfc_provider (0.0.1) + jwt (~> 2.2) + rspec (~> 3.9) + PATH remote: engines/order_management specs: diff --git a/engines/dfc_provider/app/controllers/dfc_provider/api/products_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/api/products_controller.rb index f6fc6fcc3c..ce33ba1c72 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/api/products_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/api/products_controller.rb @@ -3,11 +3,24 @@ # Controller used to provide the API products for the DFC application module DfcProvider module Api - class ProductsController < ::Api::BaseController - skip_before_filter :authenticate_user - before_filter :set_enterprise - before_filter :authenticate_user - skip_authorization_check + class ProductsController < ::ActionController::Metal + include ActionController::Head + include AbstractController::Rendering + include ActionController::Rendering + include ActionController::Renderers::All + include ActionController::MimeResponds + include ActionController::ImplicitRender + include AbstractController::Callbacks + # To access 'base_url' helper + include ActionController::UrlFor + include Rails.application.routes.url_helpers + + before_filter :check_authorization, + :check_enterprise, + :check_user, + :check_accessibility + + respond_to :json def index products = @enterprise. @@ -23,17 +36,45 @@ module DfcProvider private - def authenticate_user - @current_api_user = @enterprise.owner + def check_enterprise + @enterprise = ::Enterprise.where(id: params[:enterprise_id]).first + + return if @enterprise.present? + + head :not_found end - def set_enterprise - @enterprise = ::Enterprise.find(params[:enterprise_id]) + def check_authorization + return if access_token.present? + + head :unauthorized + end + + def check_user + @user = authorization_control.process + + return if @user.present? + + head :unprocessable_entity + end + + def check_accessibility + return if @enterprise.owner == @user + + head :forbidden end def base_url "#{root_url}api/dfc_provider" end + + def access_token + request.headers['Authorization'].to_s.split(' ').last + end + + def authorization_control + DfcProvider::AuthorizationControl.new(access_token) + end end end end diff --git a/engines/dfc_provider/app/services/dfc_provider/authorization_control.rb b/engines/dfc_provider/app/services/dfc_provider/authorization_control.rb new file mode 100644 index 0000000000..beb1885816 --- /dev/null +++ b/engines/dfc_provider/app/services/dfc_provider/authorization_control.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# Service used to authorize the user on DCF Provider API +# It controls an OICD Access token and an enterprise. +module DfcProvider + class AuthorizationControl + def initialize(access_token) + @access_token = access_token + end + + def process + decode_token + find_ofn_user + end + + def decode_token + data = JWT.decode( + @access_token, + nil, + false + ) + + @header = data.last + @payload = data.first + end + + def find_ofn_user + Spree::User.where(email: @payload['email']).first + end + end +end diff --git a/engines/dfc_provider/dfc_provider.gemspec b/engines/dfc_provider/dfc_provider.gemspec index bd3804e257..b00bdf0c33 100644 --- a/engines/dfc_provider/dfc_provider.gemspec +++ b/engines/dfc_provider/dfc_provider.gemspec @@ -6,13 +6,17 @@ $LOAD_PATH.push File.expand_path('lib', __dir__) require "dfc_provider/version" # Describe your gem and declare its dependencies: -Gem::Specification.new do |s| - s.name = 'dfc_provider' - s.version = DfcProvider::VERSION - s.authors = ['Admin OFF'] - s.email = ['admin@openfoodfrance.org'] - s.summary = 'Provides an API stack implementing DFC semantic specifications' +Gem::Specification.new do |spec| + spec.name = 'dfc_provider' + spec.version = DfcProvider::VERSION + spec.authors = ['Admin OFF'] + spec.email = ['admin@openfoodfrance.org'] + spec.summary = 'Provides an API stack implementing DFC semantic ' \ + 'specifications' - s.files = Dir["{app,config,db,lib}/**/*"] + ['README.rdoc'] - s.test_files = Dir['test/**/*'] + spec.files = Dir["{app,config,lib}/**/*"] + ['README.md'] + spec.test_files = Dir['spec/**/*'] + + spec.add_dependency 'jwt', '~> 2.2' + spec.add_dependency 'rspec', '~> 3.9' end diff --git a/engines/dfc_provider/spec/controllers/dfc_provider/api/products_controller_spec.rb b/engines/dfc_provider/spec/controllers/dfc_provider/api/products_controller_spec.rb index 9cc26b5e81..754b81fd94 100644 --- a/engines/dfc_provider/spec/controllers/dfc_provider/api/products_controller_spec.rb +++ b/engines/dfc_provider/spec/controllers/dfc_provider/api/products_controller_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe DfcProvider::Api::ProductsController, type: :controller do @@ -13,22 +15,82 @@ describe DfcProvider::Api::ProductsController, type: :controller do variant: product.variants.first, visible: true) end + let(:user) { enterprise.owner } describe('.index') do - before do - allow(controller) - .to receive(:spree_current_user) { enterprise.owner } + context 'with authorization token' do + before do + request.env['Authorization'] = 'Bearer 123456.abcdef.123456' + end - get :index, enterprise_id: enterprise.id + context 'with an enterprise' do + context 'with a linked user' do + before do + allow_any_instance_of(DfcProvider::AuthorizationControl) + .to receive(:process) + .and_return(user) + end + + context 'with valid accessibility' do + before do + get :index, enterprise_id: enterprise.id + end + + it 'is successful' do + expect(response.status).to eq 200 + end + + it 'renders the related product' do + expect(response.body) + .to include("\"DFC:description\":\"#{product.variants.first.name}\"") + end + end + + context 'without valid accessibility' do + before do + get :index, enterprise_id: create(:enterprise).id + end + + it 'returns unauthorized head' do + expect(response.status).to eq 403 + end + end + end + + context 'without a linked user' do + before do + allow_any_instance_of(DfcProvider::AuthorizationControl) + .to receive(:process) + .and_return(nil) + end + + before do + get :index, enterprise_id: enterprise.id + end + + it 'returns unprocessable_entity head' do + expect(response.status).to eq 422 + end + end + end + + context 'without a recorded enterprise' do + before do + get :index, enterprise_id: '123456' + end + + it 'returns not_found head' do + expect(response.status).to eq 404 + end + end end - it 'is successful' do - expect(response.status).to eq 200 - end + context 'without an authorization token' do + before { get :index, enterprise_id: enterprise.id } - it 'renders the related product' do - expect(response.body) - .to include("\"DFC:description\":\"#{product.variants.first.name}\"") + it 'returns unauthorized head' do + expect(response.status).to eq 401 + end end end end diff --git a/engines/dfc_provider/spec/spec_helper.rb b/engines/dfc_provider/spec/spec_helper.rb index 9cfd0bc717..3492f4f944 100644 --- a/engines/dfc_provider/spec/spec_helper.rb +++ b/engines/dfc_provider/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "../../spec/spec_helper.rb" -Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }