diff --git a/Gemfile.lock b/Gemfile.lock index e4b36060c..885506029 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,7 +70,7 @@ GEM public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) aws-eventstream (1.2.0) - aws-partitions (1.664.0) + aws-partitions (1.666.0) aws-sdk-core (3.168.1) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) @@ -85,7 +85,7 @@ GEM aws-sigv4 (~> 1.4) aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) - bootsnap (1.14.0) + bootsnap (1.15.0) msgpack (~> 1.2) builder (3.2.4) byebug (11.1.3) @@ -124,7 +124,7 @@ GEM jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jmespath (1.6.1) + jmespath (1.6.2) json (2.6.2) loofah (2.19.0) crass (~> 1.0.2) @@ -150,7 +150,7 @@ GEM nokogiri (1.13.9-x86_64-linux) racc (~> 1.4) parallel (1.22.1) - parser (3.1.2.1) + parser (3.1.3.0) ast (~> 2.4.1) pg (1.4.5) pry (0.14.1) @@ -261,7 +261,7 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - stimulus-rails (1.1.1) + stimulus-rails (1.2.0) railties (>= 6.0.0) thor (1.2.1) timeout (0.3.0) @@ -315,7 +315,7 @@ DEPENDENCIES webmock RUBY VERSION - ruby 3.1.2p20 + ruby 3.1.3p185 BUNDLED WITH - 2.3.7 + 2.3.26 diff --git a/app/models/ability.rb b/app/models/ability.rb index 1e7745e14..fdac1845a 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -4,10 +4,10 @@ class Ability include CanCan::Ability def initialize(user) - can :show, Project + can :show, Project, user_id: nil return if user.blank? - can %i[create index destroy update], Project, user_id: user + can %i[create show index destroy update], Project, user_id: user end end diff --git a/lib/concepts/project/operations/create.rb b/lib/concepts/project/operations/create.rb index 6ad3d9be1..25b3dd11e 100644 --- a/lib/concepts/project/operations/create.rb +++ b/lib/concepts/project/operations/create.rb @@ -5,7 +5,6 @@ class Create class << self def call(project_hash:) response = OperationResponse.new - response[:project] = build_project(project_hash) response[:project].save! response @@ -22,7 +21,7 @@ def build_project(project_hash) new_project = Project.new(project_hash.except(:components, :image_list).merge(identifier:)) new_project.components.build(project_hash[:components]) - project_hash[:image_list].each do |image| + (project_hash[:image_list] || []).each do |image| new_project.images.attach(image.blob) end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb new file mode 100644 index 000000000..ad3b02889 --- /dev/null +++ b/spec/models/ability_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'cancan/matchers' +require 'rails_helper' + +RSpec.describe Ability do + subject { described_class.new(user) } + + let(:project) { build(:project) } + let(:another_project) { build(:project) } + let(:starter_project) { build(:project, user_id: nil) } + + context 'when no user' do + let(:user) { nil } + + context 'with a starter project' do + it { is_expected.not_to be_able_to(:index, starter_project) } + it { is_expected.to be_able_to(:show, starter_project) } + it { is_expected.not_to be_able_to(:create, starter_project) } + it { is_expected.not_to be_able_to(:update, starter_project) } + it { is_expected.not_to be_able_to(:destroy, starter_project) } + end + + context 'with an owned project' do + it { is_expected.not_to be_able_to(:index, project) } + it { is_expected.not_to be_able_to(:show, project) } + it { is_expected.not_to be_able_to(:create, project) } + it { is_expected.not_to be_able_to(:update, project) } + it { is_expected.not_to be_able_to(:destroy, project) } + end + end + + context 'when user present' do + let(:user) { project.user_id } + + context 'with a starter project' do + it { is_expected.not_to be_able_to(:index, starter_project) } + it { is_expected.to be_able_to(:show, starter_project) } + it { is_expected.not_to be_able_to(:create, starter_project) } + it { is_expected.not_to be_able_to(:update, starter_project) } + it { is_expected.not_to be_able_to(:destroy, starter_project) } + end + + context 'with own project' do + it { is_expected.to be_able_to(:index, project) } + it { is_expected.to be_able_to(:show, project) } + it { is_expected.to be_able_to(:create, project) } + it { is_expected.to be_able_to(:update, project) } + it { is_expected.to be_able_to(:destroy, project) } + end + + context 'with another user\'s project' do + it { is_expected.not_to be_able_to(:index, another_project) } + it { is_expected.not_to be_able_to(:show, another_project) } + it { is_expected.not_to be_able_to(:create, another_project) } + it { is_expected.not_to be_able_to(:update, another_project) } + it { is_expected.not_to be_able_to(:destroy, another_project) } + end + end +end diff --git a/spec/request/projects/show_spec.rb b/spec/request/projects/show_spec.rb index ebdf9c4aa..019d5eb56 100644 --- a/spec/request/projects/show_spec.rb +++ b/spec/request/projects/show_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Project show requests', type: :request do let!(:project) { create(:project) } - let(:expected_json) do + let(:project_json) do { identifier: project.identifier, project_type: 'python', @@ -15,25 +15,102 @@ }.to_json end - it 'returns success response' do - get "/api/projects/#{project.identifier}" + context 'when user is logged in' do + before do + mock_oauth_user(project.user_id) + end - expect(response).to have_http_status(:ok) - end + context 'when loading own project' do + it 'returns success response' do + get "/api/projects/#{project.identifier}" - it 'returns json' do - get "/api/projects/#{project.identifier}" - expect(response.content_type).to eq('application/json; charset=utf-8') - end + expect(response).to have_http_status(:ok) + end + + it 'returns json' do + get "/api/projects/#{project.identifier}" + expect(response.content_type).to eq('application/json; charset=utf-8') + end + + it 'returns the project json' do + get "/api/projects/#{project.identifier}" + expect(response.body).to eq(project_json) + end + end + + context 'when loading another user\'s project' do + let!(:another_project) { create(:project) } + let(:another_project_json) do + { + identifier: another_project.identifier, + project_type: 'python', + name: another_project.name, + user_id: another_project.user_id, + components: [], + image_list: [] + }.to_json + end + + it 'returns forbidden response' do + get "/api/projects/#{another_project.identifier}" + + expect(response).to have_http_status(:forbidden) + end - it 'returns the project json' do - get "/api/projects/#{project.identifier}" - expect(response.body).to eq(expected_json) + it 'does not return the project json' do + get "/api/projects/#{another_project.identifier}" + expect(response.body).not_to include(another_project_json) + end + end end - it 'returns 404 response if invalid project' do - get '/api/projects/no-such-project' + context 'when user is not logged in' do + context 'when loading a starter project' do + let!(:starter_project) { create(:project, user_id: nil) } + let(:starter_project_json) do + { + identifier: starter_project.identifier, + project_type: 'python', + name: starter_project.name, + user_id: starter_project.user_id, + components: [], + image_list: [] + }.to_json + end + + it 'returns success response' do + get "/api/projects/#{starter_project.identifier}" + + expect(response).to have_http_status(:ok) + end + + it 'returns json' do + get "/api/projects/#{starter_project.identifier}" + expect(response.content_type).to eq('application/json; charset=utf-8') + end + + it 'returns the project json' do + get "/api/projects/#{starter_project.identifier}" + expect(response.body).to eq(starter_project_json) + end + + it 'returns 404 response if invalid project' do + get '/api/projects/no-such-project' + expect(response).to have_http_status(:not_found) + end + end + + context 'when loading an owned project' do + it 'returns forbidden response' do + get "/api/projects/#{project.identifier}" + + expect(response).to have_http_status(:forbidden) + end - expect(response).to have_http_status(:not_found) + it 'does not return the project json' do + get "/api/projects/#{project.identifier}" + expect(response.body).not_to include(project_json) + end + end end end diff --git a/tmp/pids/.keep b/tmp/pids/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/tmp/storage/.keep b/tmp/storage/.keep deleted file mode 100644 index e69de29bb..000000000