From 13eae287bb536803fb3adb9a42badb6730bdb0ca Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Mon, 7 Aug 2017 11:54:45 +0200 Subject: [PATCH] Revert "Add core API using JWT authentication and tests for it [SCI-1507]" --- Gemfile | 3 - Gemfile.lock | 2 - app/controllers/api/api_controller.rb | 84 ------------ .../api/v20170715/core_api_controller.rb | 53 ------- app/services/api.rb | 25 ---- app/services/api/core_jwt.rb | 32 ----- config/initializers/core_api.rb | 11 -- config/initializers/extends.rb | 3 - config/locales/en.yml | 6 - config/routes.rb | 9 -- package.json | 2 +- spec/controllers/api/api_controller_spec.rb | 65 --------- .../api/v20170715/core_api_controller_spec.rb | 129 ------------------ spec/factories/users.rb | 3 - spec/rails_helper.rb | 4 - spec/support/api_helper.rb | 17 --- 16 files changed, 1 insertion(+), 447 deletions(-) delete mode 100644 app/controllers/api/api_controller.rb delete mode 100644 app/controllers/api/v20170715/core_api_controller.rb delete mode 100644 app/services/api.rb delete mode 100644 app/services/api/core_jwt.rb delete mode 100644 config/initializers/core_api.rb delete mode 100644 spec/controllers/api/api_controller_spec.rb delete mode 100644 spec/controllers/api/v20170715/core_api_controller_spec.rb delete mode 100644 spec/support/api_helper.rb diff --git a/Gemfile b/Gemfile index ffa953863..ea3ad1fa5 100644 --- a/Gemfile +++ b/Gemfile @@ -17,9 +17,6 @@ gem 'font-awesome-rails', '~> 4.7.0.2' gem 'recaptcha', require: 'recaptcha/rails' gem 'sanitize', '~> 4.4' -# Gems for API implementation -gem 'jwt' - # JS datetime library, requirement of datetime picker gem 'momentjs-rails', '~> 2.17.1' # JS datetime picker diff --git a/Gemfile.lock b/Gemfile.lock index 24eeb6100..91e9d8ee2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -210,7 +210,6 @@ GEM js_cookie_rails (2.1.4) railties (>= 3.1) json (1.8.6) - jwt (1.5.6) kaminari (1.0.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.0.1) @@ -489,7 +488,6 @@ DEPENDENCIES jquery-turbolinks jquery-ui-rails js_cookie_rails - jwt kaminari listen (~> 3.0) logging (~> 2.0.0) diff --git a/app/controllers/api/api_controller.rb b/app/controllers/api/api_controller.rb deleted file mode 100644 index e7aebdf24..000000000 --- a/app/controllers/api/api_controller.rb +++ /dev/null @@ -1,84 +0,0 @@ -module Api - class ApiController < ActionController::API - attr_reader :authorized - attr_reader :iss - attr_reader :token - attr_reader :current_user - - before_action :load_token, except: %i(authenticate status) - before_action :load_iss, except: %i(authenticate status) - before_action :authenticate_request!, except: %i(authenticate status) - - rescue_from StandardError do - render json: {}, status: :bad_request - end - - rescue_from JWT::InvalidPayload, JWT::DecodeError do - render json: { message: I18n.t('api.core.invalid_token') }, - status: :forbidden - end - - def initialize - super - @iss = nil - @authorized = false - end - - def status - response = {} - response[:message] = I18n.t('api.core.status_ok') - response[:versions] = [] - Extends::API_VERSIONS.each do |ver| - response[:versions] << { version: ver, baseUrl: "/api/#{ver}/" } - end - render json: response, status: :ok - end - - def authenticate - if auth_params[:grant_type] == 'password' - user = User.find_by_email(auth_params[:email]) - raise StandardError unless user && - user.valid_password?(auth_params[:password]) - payload = { user_id: user.id } - token = CoreJwt.encode(payload) - render json: { token_type: 'bearer', access_token: token } - else - raise StandardError - end - end - - private - - def load_token - @token = - request.headers['Authorization'].scan(/Bearer (.*)$/).flatten.last - raise StandardError unless @token - end - - def authenticate_request! - return true if authorized - # Check request header for proper auth token - payload = CoreJwt.decode(token) - @current_user = User.find_by_id(payload['user_id']) - # Implement sliding sessions, i.e send new token in case of successful - # authorization and when tokens TTL reached specific value (to avoid token - # generation on each request) - if CoreJwt.refresh_needed?(payload) - new_token = CoreJwt.encode(user_id: @current_user.id) - response.headers['X-Access-Token'] = new_token - end - rescue JWT::ExpiredSignature - render json: { message: I18n.t('api.core.expired_token') }, - status: :unauthorized - end - - def load_iss - @iss = CoreJwt.read_iss(token) - raise JWT::InvalidPayload unless @iss - end - - def auth_params - params.permit(:grant_type, :email, :password) - end - end -end diff --git a/app/controllers/api/v20170715/core_api_controller.rb b/app/controllers/api/v20170715/core_api_controller.rb deleted file mode 100644 index e06705d9e..000000000 --- a/app/controllers/api/v20170715/core_api_controller.rb +++ /dev/null @@ -1,53 +0,0 @@ -module Api - module V20170715 - class CoreApiController < ApiController - include PermissionHelper - - def tasks_tree - teams_json = [] - current_user.teams.find_each do |tm| - team = tm.as_json(only: %i(name description)) - team['team_id'] = tm.id.to_s - projects = [] - tm.projects.find_each do |pr| - project = pr.as_json(only: %i(name visibility archived)) - project['project_id'] = pr.id.to_s - experiments = [] - pr.experiments.find_each do |exp| - experiment = exp.as_json(only: %i(name description archived)) - experiment['experiment_id'] = exp.id.to_s - tasks = [] - exp.my_modules.find_each do |tk| - task = tk.as_json(only: %i(name description archived)) - task['task_id'] = tk.id.to_s - tasks << task - end - experiment['tasks'] = tasks - experiments << experiment - end - project['experiments'] = experiments - projects << project - end - team['projects'] = projects - teams_json << team - end - render json: teams_json, status: :ok - end - - def task_samples - task = MyModule.find_by_id(params[:task_id]) - return render json: {}, status: :not_found unless task - return render json: {}, status: :forbidden unless can_view_module(task) - samples = task.samples - samples_json = [] - samples.find_each do |s| - sample = {} - sample['sample_id'] = s.id.to_s - sample['name'] = s.name - samples_json << sample - end - render json: samples_json, status: :ok - end - end - end -end diff --git a/app/services/api.rb b/app/services/api.rb deleted file mode 100644 index da7998464..000000000 --- a/app/services/api.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Api - class << self - attr_accessor :configuration - end - - def self.configuration - @configuration ||= Configuration.new - end - - def self.configure - yield(configuration) - end - - class Configuration - attr_accessor :core_api_sign_alg - attr_accessor :core_api_token_ttl - attr_accessor :core_api_token_iss - - def initialize - @core_api_sign_alg = 'HS256' - @core_api_token_ttl = 30.minutes - @core_api_token_iss = 'SciNote' - end - end -end diff --git a/app/services/api/core_jwt.rb b/app/services/api/core_jwt.rb deleted file mode 100644 index a7083c765..000000000 --- a/app/services/api/core_jwt.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Api - class CoreJwt - require 'jwt' - KEY_SECRET = Rails.application.secrets.secret_key_base - - def self.encode(payload, expires_at = nil) - if expires_at - payload[:exp] = expires_at - else - payload[:exp] = Api.configuration.core_api_token_ttl.from_now.to_i - end - payload[:iss] = Api.configuration.core_api_token_iss - JWT.encode(payload, KEY_SECRET, Api.configuration.core_api_sign_alg) - end - - def self.decode(token) - HashWithIndifferentAccess.new( - JWT.decode(token, KEY_SECRET, Api.configuration.core_api_sign_alg)[0] - ) - end - - def self.read_iss(token) - JWT.decode(token, nil, false)[0][:iss].to_s - end - - def self.refresh_needed?(payload) - time_left = payload[:exp].to_i - Time.now.to_i - return true if time_left < (Api.configuration.core_api_token_ttl.to_i / 2) - false - end - end -end diff --git a/config/initializers/core_api.rb b/config/initializers/core_api.rb deleted file mode 100644 index c76ddf54d..000000000 --- a/config/initializers/core_api.rb +++ /dev/null @@ -1,11 +0,0 @@ -Api.configure do |config| - if ENV['CORE_API_SIGN_ALG'] - config.core_api_sign_alg = ENV['CORE_API_SIGN_ALG'] - end - if ENV['CORE_API_TOKEN_TTL'] - config.core_api_token_ttl = ENV['CORE_API_TOKEN_TTL'] - end - if ENV['CORE_API_TOKEN_ISS'] - config.core_api_token_iss = ENV['CORE_API_TOKEN_ISS'] - end -end diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb index 0279bc635..b5a9d2b05 100644 --- a/config/initializers/extends.rb +++ b/config/initializers/extends.rb @@ -42,7 +42,4 @@ class Extends # Data type name should match corresponding model's name REPOSITORY_DATA_TYPES = { RepositoryTextValue: 0, RepositoryDateValue: 1 } - - # List of implemented core API versions - API_VERSIONS = ['20170715'] end diff --git a/config/locales/en.yml b/config/locales/en.yml index 19fd29852..24498ed3c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1786,12 +1786,6 @@ en: busy: "The server is still processing your request. If you leave this page, the changes will be lost! Are you sure you want to continue?" no_name: "(no name)" - api: - core: - status_ok: "Ok" - expired_token: "Token is expired" - invalid_token: "Token is invalid" - Add: "Add" Asset: "File" Assets: "Files" diff --git a/config/routes.rb b/config/routes.rb index b1c8c4bf5..7aed6b3f9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -472,15 +472,6 @@ Rails.application.routes.draw do post 'avatar_signature' => 'users/registrations#signature' get 'users/auth_token_sign_in' => 'users/sessions#auth_token_create' end - - namespace :api, defaults: { format: 'json' } do - get 'status', to: 'api#status' - post 'auth/token', to: 'api#authenticate' - scope '20170715', module: 'v20170715' do - get 'tasks/tree', to: 'core_api#tasks_tree' - get 'tasks/:task_id/samples', to: 'core_api#task_samples' - end - end end constraints WopiSubdomain do diff --git a/package.json b/package.json index 20fe5a4e5..57fc1ccc9 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "lint": "eslint ." }, "devDependencies": { - "eslint": "^3.19.0", + "eslint": "^3.7.1", "eslint-config-google": "^0.5.0", "eslint-plugin-react": "^7.1.0", "webpack-dev-server": "^2.5.1" diff --git a/spec/controllers/api/api_controller_spec.rb b/spec/controllers/api/api_controller_spec.rb deleted file mode 100644 index 82e6d28fb..000000000 --- a/spec/controllers/api/api_controller_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'rails_helper' - -describe Api::ApiController, type: :controller do - describe 'GET #status' do - before do - get :status - end - - it 'Returns HTTP success' do - expect(response).to be_success - expect(response).to have_http_status(200) - end - - it 'Response with correct JSON status structure' do - hash_body = nil - expect { hash_body = json }.not_to raise_exception - expect(hash_body).to match( - 'message' => I18n.t('api.core.status_ok'), - 'versions' => [{ 'version' => '20170715', - 'baseUrl' => '/api/20170715/' }] - ) - end - end - - describe 'Post #authenticate' do - let(:user) { create(:user) } - - context 'When valid request' do - before do - post :authenticate, params: { email: user.email, - password: user.password, - grant_type: 'password' } - end - - it 'Returns HTTP success' do - expect(response).to have_http_status(200) - end - - it 'Returns valid JWT token' do - token = nil - expect { token = json['access_token'] }.not_to raise_exception - user_id = nil - expect { user_id = decode_token(token) }.not_to raise_exception - expect(user_id).to eq(user.id) - end - end - - context 'When invalid password in request' do - it 'Returns HTTP error' do - post :authenticate, params: { email: user.email, - password: 'wrong_password', - grant_type: 'password' } - expect(response).to have_http_status(400) - end - end - - context 'When no grant_type in request' do - it 'Returns HTTP error' do - post :authenticate, params: { email: user.email, - password: user.password } - expect(response).to have_http_status(400) - end - end - end -end diff --git a/spec/controllers/api/v20170715/core_api_controller_spec.rb b/spec/controllers/api/v20170715/core_api_controller_spec.rb deleted file mode 100644 index 8c5fe596d..000000000 --- a/spec/controllers/api/v20170715/core_api_controller_spec.rb +++ /dev/null @@ -1,129 +0,0 @@ -require 'rails_helper' - -describe Api::V20170715::CoreApiController, type: :controller do - let(:user) { create(:user) } - let(:task) { create(:my_module) } - let(:sample1) { create(:sample) } - let(:sample2) { create(:sample) } - let(:sample3) { create(:sample) } - before do - task.samples << [sample1, sample2, sample3] - UserProject.create!(user: user, project: task.experiment.project, role: 0) - end - - describe 'GET #task_samples' do - context 'When valid request' do - before do - request.headers['HTTP_ACCEPT'] = 'application/json' - request.headers['Authorization'] = 'Bearer ' + generate_token(user.id) - get :task_samples, params: { task_id: task.id } - end - - it 'Returns HTTP success' do - expect(response).to have_http_status(200) - end - - it 'Returns JSON body containing expected Samples' do - hash_body = nil - expect { hash_body = json }.not_to raise_exception - expect(hash_body).to match( - [{ 'sample_id' => sample1.id.to_s, 'name' => sample1.name }, - { 'sample_id' => sample2.id.to_s, 'name' => sample2.name }, - { 'sample_id' => sample3.id.to_s, 'name' => sample3.name }] - ) - end - end - - context 'When invalid request' do - context 'When invalid token' do - before do - request.headers['HTTP_ACCEPT'] = 'application/json' - request.headers['Authorization'] = 'Bearer WroNgToken' - get :task_samples, params: { task_id: task.id } - end - - it 'Returns HTTP access denied' do - expect(response).to have_http_status(403) - end - end - - context 'When valid token, but invalid request' do - before do - request.headers['HTTP_ACCEPT'] = 'application/json' - request.headers['Authorization'] = 'Bearer ' + generate_token(user.id) - end - - it 'Returns HTTP not found' do - get :task_samples, params: { task_id: 1000 } - expect(response).to have_http_status(404) - expect(json).to match({}) - end - - it 'Returns HTTP access denied' do - UserProject.where(user: user, project: task.experiment.project) - .first - .destroy! - get :task_samples, params: { task_id: task.id } - expect(response).to have_http_status(403) - expect(json).to match({}) - end - end - end - end - - describe 'GET #tasks_tree' do - context 'When valid request' do - before do - request.headers['HTTP_ACCEPT'] = 'application/json' - request.headers['Authorization'] = 'Bearer ' + generate_token(user.id) - get :tasks_tree - end - - it 'Returns HTTP success' do - expect(response).to have_http_status(200) - end - - it 'Returns JSON body containing expected Task tree' do - team = user.teams.first - experiment = task.experiment - project = experiment.project - hash_body = nil - expect { hash_body = json }.not_to raise_exception - expect(hash_body).to match( - ['team_id' => team.id.to_s, 'name' => team.name, - 'description' => team.description, - 'projects' => [{ - 'project_id' => project.id.to_s, 'name' => project.name, - 'visibility' => project.visibility, - 'archived' => project.archived, - 'experiments' => [{ - 'experiment_id' => experiment.id.to_s, - 'name' => experiment.name, - 'description' => experiment.description, - 'archived' => experiment.archived, - 'tasks' => [{ - 'task_id' => task.id.to_s, 'name' => task.name, - 'description' => task.description, - 'archived' => task.archived - }] - }] - }]] - ) - end - end - - context 'When invalid request' do - context 'When invalid token' do - before do - request.headers['HTTP_ACCEPT'] = 'application/json' - request.headers['Authorization'] = 'Bearer WroNgToken' - get :tasks_tree - end - - it 'Returns HTTP access denied' do - expect(response).to have_http_status(403) - end - end - end - end -end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index b60595471..fc1a11700 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -5,8 +5,5 @@ FactoryGirl.define do email 'admin_test@scinote.net' password 'asdf1243' password_confirmation 'asdf1243' - after(:create) do |user| - user.teams << (Team.first || FactoryGirl.create(:team)) - end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 797bc18bb..32c2525a0 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -9,8 +9,6 @@ abort('The Rails environment is running in production mode!') if Rails.env.produ require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! -Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } - # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end @@ -80,8 +78,6 @@ RSpec.configure do |config| # includes FactoryGirl in rspec config.include FactoryGirl::Syntax::Methods - config.include Devise::Test::ControllerHelpers, type: :controller - config.include ApiHelper, type: :controller end # config shoulda matchers to work with rspec diff --git a/spec/support/api_helper.rb b/spec/support/api_helper.rb deleted file mode 100644 index fbbd15928..000000000 --- a/spec/support/api_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module ApiHelper - def generate_token(user_id) - Api::CoreJwt.encode(user_id: user_id) - end - - def generate_expired_token(user_id) - Api::CoreJwt.encode({ user_id: user_id }, (Time.now.to_i - 300)) - end - - def decode_token(token) - Api::CoreJwt.decode(token)['user_id'].to_i - end - - def json - JSON.parse(response.body) - end -end