require 'active_support/concern' require 'oauth2' module ShopliftClient extend ActiveSupport::Concern include Shopapp3 included do attr_reader :authentication attr_reader :api_key attr_reader :current_user attr_reader :current_company attr_reader :do_hide_search_for_this_action attr_reader :search_path attr_accessor :search_placeholder helper_method :company_logo_path_defined helper_method :supported_locales_defined helper_method :home_link_path end class_methods do attr_reader :search_path attr_reader :required_scopes attr_reader :do_hide_search_for_this_controller attr_reader :search_placeholder def set_search_path(value) @search_path = value return if @search_path == :current_path @search_path = "/#{@search_path}" unless @search_path[0] == '/' @search_path = "#{@search_path}/" unless @search_path[-1] == '/' end def hide_search_for_this_controller @do_hide_search_for_this_controller = true end def show_search_for_this_controller(params = {}) @do_hide_search_for_this_controller = false @search_placeholder = params[:placeholder] if params[:placeholder].present? @search_path = params[:search_path] if params[:search_path].present? @search_remote = params[:search_remote] if params[:search_remote].present? end def require_scopes(scopes) @required_scopes ||= [] scopes = [scopes] unless scopes.is_a? Array @required_scopes.concat scopes.map(&:to_s) @required_scopes.uniq! end end def company_info(company_id = nil) @company_info ||= {} return @company_info[company_id] if @company_info[company_id].present? @company_info[company_id] = JSON.parse srv.get(['/api/users/company_info', company_id].compact.join '/').body ["clients", "suppliers"].each do |partner_type| @company_info[company_id][partner_type].each do |partner| partner[:company] = Company.find_or_create_by! code: partner['code'] partner[:company].name = partner['name'] partner[:company].info ||= {} # Very ugly bugfix: investigate how comes this is string in the first place if partner[:company].info.is_a? String begin partner[:company].info = JSON.parse(partner[:company].info) rescue end end partner[:company].info['company_info'] = partner['info'] partner[:company].save! end end @company_info[company_id] end def hide_search_for_this_action @do_hide_search_for_this_action = true end def search_remote @search_remote || false end def show_search_for_this_action(params = {}) @do_hide_search_for_this_action = false @search_placeholder = params[:placeholder] if params[:placeholder].present? @search_path = params[:search_path] if params[:search_path].present? @search_remote = params[:search_remote] if params[:search_remote].present? end def session_cookie session["authlift_session_id"] end def session_cookie=(new_value) session["authlift_session_id"] = new_value end def local_authlift_redirect_uri if respond_to? :app_authlift_redirect_uri app_authlift_redirect_uri else Rails.configuration.settings['authlift_redirect_uri'] end end def redirect_unauthorized return if performed? session.clear session[:previous_url] = request.fullpath redirect_to client.auth_code.authorize_url( redirect_uri: local_authlift_redirect_uri, scope: scope) end def scope [Rails.configuration.settings['authlift_default_scope'], 'public'].compact.join ' ' end def find_company_by_code(code, parameters = {}) begin @current_company ||= Company.find_or_create_by! code: code do |new_company| fail if parameters.empty? new_company.name = parameters[:name] # following is an ugly yet backwards conpatible and safe way to store # the company info if and in the best way possible, until all the apps # are updated to have json there. case new_company.column_for_attribute('info').type when :json new_company.info = parameters when :string new_company.info = parameters.to_json end end rescue ActiveRecord::StatementInvalid if $!.cause.is_a? PG::UndefinedTable fail <<-ERROR.strip_heredoc You have not defined a company, and that is compulsory even if you are not planning to add any additional fields. You do not need to seed it, so following is enough forever: rails g model company code:string name:string info:json; rake db:migrate ERROR end end end def authenticate_user if session_cookie.present? @token = OAuth2::AccessToken.new client, session_cookie, scope: scope begin x = srv.get '/api/users/profile' @current_user_json_hash = @current_user = JSON.parse x.response.body unless @current_user['scopes'].is_a? String user_scopes = @current_user['scopes'] else user_scopes = JSON.parse @current_user['scopes'] end unless user_scopes.include? 'admin' (self.class.required_scopes || []).each do |required_scope| unless user_scopes.include? required_scope render(file: 'shopapp/403.html', status: 403, layout: false, locals: { missing_scope: required_scope }) return false end end end find_company_by_code current_user['company']['code'], name: current_user['company']['name'], logo_code: current_user['company']['logo_code'] rescue OAuth2::Error return false end else return false end true end def authenticate_user! redirect_unauthorized unless authenticate_user end def authenticate_user_or_api! unless authenticate_company!(true) redirect_unauthorized end end def handle_not_authorized(message) if request.format.html? redirect_unauthorized else fail ActionController::RoutingError, message end end def authenticate_company!(soft = false) return true if authenticate_user @api_key = if params['key'].present? params['key'].match(/[0-9a-f]+/).to_s elsif request.headers['AUTHORIZATION'].present? && !request.headers['AUTHORIZATION'].include?('Basic') request.headers['AUTHORIZATION'].gsub(/^Bearer ?/, '') else Rails.configuration.settings['authlift_default_app_key'] end if @api_key.blank? return false if soft handle_not_authorized 'Authentication token missing' end response = srv.post 'auth/api_key', body: { api_key: api_key, requested_action: "#{self.controller_name}##{self.action_name}" } if response.blank? return false if soft handle_not_authorized 'Request not authorized' end @authentication = JSON.parse response.body find_company_by_code authentication['company'] true end def current_user_json current_user.to_json end def current_auditor current_user.to_h['email'] end def current_user return @current_user if @current_user.present? @current_user end def current_company end def user_signed_in? !current_user.nil? end # To create/update a model, params must be of form { model_name: { attr1: value1, attr2: value2 } } # and attr1, attr2 must be in the list of allowed params the Rails way. def post(url, params) puts 'co_cli: post' puts "url: #{url}" puts "params: #{params}" response = srv.request(:post, url, body: params) JSON.parse(response.body) rescue OAuth2::Error raise "Server fault, could not perform post to #{srv.client.site}#{url}" rescue raise "Unknown error, could not perform post to #{srv.client.site}#{url}" end def get(url, params = {}) puts 'co_cli: get' puts "url: #{url}" puts "params: #{params}" response = srv.request(:get, url, body: params) JSON.parse(response.body) rescue OAuth2::Error raise "Server fault, could not perform post to #{srv.client.site}#{url}" rescue raise "Unknown error, could not perform post to #{srv.client.site}#{url}" end def srv @token ||= client.client_credentials.get_token scope: scope end def client @oauth ||= OAuth2::Client.new Rails.configuration.settings['authlift_app_id'], Rails.configuration.settings['authlift_app_secret'], site: Rails.configuration.settings['authlift_url'] end def company_logo_path_defined if defined? self.company_logo_path company_logo_path else "https://media.shoplift.fi/company_logos/#{@current_user_json_hash['company']['logo_code']}_company_logo_24.png" end end def supported_locales_defined if defined? self.supported_locales supported_locales else [] end end def home_link_path root_path end end