mumuki-laboratory 5.10.0 → 5.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/application/modules/_console.scss +1 -1
- data/app/controllers/api/base_controller.rb +1 -1
- data/app/models/api_client.rb +6 -2
- data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +33 -8
- data/lib/mumuki/laboratory/mumukit/platform.rb +12 -0
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/spec/controllers/api_clients_controller.rb +1 -1
- data/spec/controllers/courses_api_controller_spec.rb +1 -1
- data/spec/controllers/organizations_api_controller_spec.rb +11 -7
- data/spec/controllers/students_api_controller_spec.rb +1 -1
- data/spec/controllers/users_api_controller_spec.rb +1 -1
- data/spec/features/not_found_private_flow_spec.rb +43 -0
- data/spec/features/{not_found_flow_spec.rb → not_found_public_flow_spec.rb} +0 -0
- data/spec/spec_helper.rb +6 -2
- metadata +6 -5
- data/app/controllers/concerns/with_api_errors.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42dc9471f1a0a74ace505a73db807a18852b0347f79059953eb5c46a2756fc73
|
4
|
+
data.tar.gz: e03f2a3eb2f5037cbe01d12847cd0c9bf200ce8d6c5729f7c20536837f09f8c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e36aac69358d75f9eceb7058c5fcbf38041acdc8be060aa0703bffab770175277a3b5b6bb669ce19118ce0c7b3cf95c9034584476e907d604e38a9eb583d2ce
|
7
|
+
data.tar.gz: e10e71a29c00f626bf47a5e2ab008bee671638bc57e8b633e9af62025e4edd330fb550014084e7f173f85ef1b5e64613da3465d6b2e65fc111ec4e158e383344
|
data/app/models/api_client.rb
CHANGED
@@ -9,12 +9,12 @@ class ApiClient < ApplicationRecord
|
|
9
9
|
before_create :set_encoded_token!
|
10
10
|
|
11
11
|
def verify!
|
12
|
-
|
12
|
+
self.class.invalid_token! 'Invalid Api Client' if Mumukit::Auth::Token.decode(token).uid != user.uid
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.verify_token!(token)
|
16
16
|
client = find_by token: token
|
17
|
-
|
17
|
+
invalid_token! 'No Api Client found for Token' unless client
|
18
18
|
client.verify!
|
19
19
|
end
|
20
20
|
|
@@ -24,4 +24,8 @@ class ApiClient < ApplicationRecord
|
|
24
24
|
self.token = Mumukit::Auth::Token.encode user.uid, {}
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.invalid_token!(message)
|
28
|
+
raise Mumukit::Auth::InvalidTokenError, message
|
29
|
+
end
|
30
|
+
|
27
31
|
end
|
@@ -8,37 +8,62 @@ module Mumuki::Laboratory::Controllers::DynamicErrors
|
|
8
8
|
end
|
9
9
|
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
10
10
|
rescue_from Mumukit::Auth::UnauthorizedAccessError, with: :forbidden
|
11
|
+
rescue_from Mumukit::Auth::InvalidTokenError, with: :unauthorized
|
11
12
|
rescue_from Mumuki::Laboratory::NotFoundError, with: :not_found
|
12
13
|
rescue_from Mumuki::Laboratory::ForbiddenError, with: :forbidden
|
13
14
|
rescue_from Mumuki::Laboratory::UnauthorizedError, with: :unauthorized
|
14
15
|
rescue_from Mumuki::Laboratory::GoneError, with: :gone
|
15
16
|
rescue_from Mumuki::Laboratory::BlockedForumError, with: :blocked_forum
|
17
|
+
rescue_from ActiveRecord::RecordInvalid, with: :bad_record
|
18
|
+
end
|
19
|
+
|
20
|
+
def bad_record(exception)
|
21
|
+
# bad records can only be produced thourgh API
|
22
|
+
render_api_errors exception.record.errors, 400
|
16
23
|
end
|
17
24
|
|
18
25
|
def not_found
|
19
|
-
|
26
|
+
render_error 'not_found', 404, formats: [:html]
|
20
27
|
end
|
21
28
|
|
22
29
|
def internal_server_error(exception)
|
23
30
|
Rails.logger.error "Internal server error: #{exception} \n#{exception.backtrace.join("\n")}"
|
24
|
-
|
31
|
+
render_error 'internal_server_error', 500
|
25
32
|
end
|
26
33
|
|
27
|
-
def unauthorized
|
28
|
-
|
34
|
+
def unauthorized(exception)
|
35
|
+
render_error 'unauthorized', 401, error_message: exception.message
|
29
36
|
end
|
30
37
|
|
31
38
|
def forbidden
|
32
|
-
|
33
|
-
|
39
|
+
message = "Access to organization #{Organization.current} was forbidden to user #{current_user.uid} with permissions #{current_user.permissions.to_json}"
|
40
|
+
Rails.logger.info message
|
41
|
+
render_error 'forbidden', 403, locals: { explanation: :forbidden_explanation }, error_message: message
|
34
42
|
end
|
35
43
|
|
36
44
|
def blocked_forum
|
37
|
-
|
45
|
+
render_error 'forbidden', 403, locals: { explanation: :blocked_forum_explanation }
|
38
46
|
end
|
39
47
|
|
40
48
|
def gone
|
41
|
-
|
49
|
+
render_error 'gone', 410
|
50
|
+
end
|
51
|
+
|
52
|
+
def render_error(template, status, options={})
|
53
|
+
if Mumukit::Platform.organization_mapping.path_under_namespace? request.path, 'api'
|
54
|
+
render_api_errors [options[:error_message] || template.gsub('_', ' ')], status
|
55
|
+
else
|
56
|
+
render_app_errors template, options.merge(status: status).except(:error_message)
|
57
|
+
end
|
42
58
|
end
|
43
59
|
|
60
|
+
private
|
61
|
+
|
62
|
+
def render_app_errors(template, options)
|
63
|
+
render "errors/#{template}", options
|
64
|
+
end
|
65
|
+
|
66
|
+
def render_api_errors(errors, status)
|
67
|
+
render json: { errors: errors }, status: status
|
68
|
+
end
|
44
69
|
end
|
@@ -18,6 +18,14 @@ class Mumuki::Laboratory::Engine < ::Rails::Engine
|
|
18
18
|
config.i18n.available_locales = Mumukit::Platform::Locale.supported
|
19
19
|
end
|
20
20
|
|
21
|
+
module Mumukit::Platform::OrganizationMapping::Subdomain
|
22
|
+
class << self
|
23
|
+
def path_under_namespace?(path, namespace)
|
24
|
+
path.start_with? "/#{namespace}/"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
21
29
|
module Mumukit::Platform::OrganizationMapping::Path
|
22
30
|
class << self
|
23
31
|
alias __organization_name__ organization_name
|
@@ -30,5 +38,9 @@ module Mumukit::Platform::OrganizationMapping::Path
|
|
30
38
|
name
|
31
39
|
end
|
32
40
|
end
|
41
|
+
|
42
|
+
def path_under_namespace?(path, namespace)
|
43
|
+
path.start_with? "/#{Mumukit::Platform.current_organization_name}/#{namespace}/"
|
44
|
+
end
|
33
45
|
end
|
34
46
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe ApiClientsController, type: :controller, organization_workspace: :base do
|
4
|
-
before {
|
4
|
+
before { set_api_client! api_client }
|
5
5
|
let(:api_client) { create :api_client }
|
6
6
|
let(:api_client_json) do
|
7
7
|
{description: 'foo',
|
@@ -6,18 +6,22 @@ describe Api::OrganizationsController, type: :controller, organization_workspace
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'unauthenticated request' do
|
9
|
-
|
9
|
+
before { get :index }
|
10
|
+
it { expect(response.body).to json_eq errors: ['missing authorization header'] }
|
11
|
+
it { check_status! 401 }
|
10
12
|
end
|
11
13
|
|
12
|
-
describe '
|
13
|
-
before {
|
14
|
-
|
14
|
+
describe 'invalid authenticated request' do
|
15
|
+
before { set_token! 'foo' }
|
16
|
+
before { get :index }
|
17
|
+
it { expect(response.body).to json_eq errors: ['No Api Client found for Token'] }
|
18
|
+
it { check_status! 401 }
|
15
19
|
end
|
16
20
|
|
17
21
|
describe 'authenticated request' do
|
18
22
|
let(:book) { create :book }
|
19
23
|
|
20
|
-
before { set_api_client! }
|
24
|
+
before { set_api_client! api_client }
|
21
25
|
|
22
26
|
describe 'GET' do
|
23
27
|
let!(:public_organization) { create :organization, name: 'public' }
|
@@ -33,7 +37,7 @@ describe Api::OrganizationsController, type: :controller, organization_workspace
|
|
33
37
|
|
34
38
|
it { check_status! 200 }
|
35
39
|
|
36
|
-
it { expect(body[:organizations].map { |it| it[:name] }).to contain_exactly
|
40
|
+
it { expect(body[:organizations].map { |it| it[:name] }).to contain_exactly 'base', 'public', 'dot.org', 'private', 'another_private' }
|
37
41
|
end
|
38
42
|
context 'with non-wildcard permissions' do
|
39
43
|
before { get :index }
|
@@ -42,7 +46,7 @@ describe Api::OrganizationsController, type: :controller, organization_workspace
|
|
42
46
|
|
43
47
|
it { check_status! 200 }
|
44
48
|
|
45
|
-
it { expect(body[:organizations].map { |it| it[:name] }).to
|
49
|
+
it { expect(body[:organizations].map { |it| it[:name] }).to contain_exactly 'public', 'private' }
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
[:student, :teacher].each do |role|
|
4
4
|
|
5
5
|
describe "Api::#{role.capitalize}sController".constantize, type: :controller, organization_workspace: :base do
|
6
|
-
before { set_api_client! }
|
6
|
+
before { set_api_client! api_client }
|
7
7
|
let(:api_client) { create :api_client, grant: 'anorganization/*' }
|
8
8
|
let(:json) { {
|
9
9
|
first_name: 'Agustin',
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
feature 'not found on app', organization_workspace: :base do
|
4
|
+
before { set_subdomain_host! Organization.base.name }
|
5
|
+
before { Organization.base.switch! }
|
6
|
+
|
7
|
+
let(:owner) { create(:user, permissions: {owner: '*'}) }
|
8
|
+
let(:student_api_client) { create :api_client, role: :student, grant: 'central/*' }
|
9
|
+
let(:owner_api_client) { create :api_client, role: :owner, grant: '*' }
|
10
|
+
|
11
|
+
scenario 'app without authentication' do
|
12
|
+
visit '/nonexistentroute'
|
13
|
+
|
14
|
+
expect(page).to have_text 'You are not allowed to see this content'
|
15
|
+
end
|
16
|
+
|
17
|
+
scenario 'app with authentication' do
|
18
|
+
set_current_user! owner
|
19
|
+
|
20
|
+
visit '/nonexistentroute'
|
21
|
+
|
22
|
+
expect(page).to have_text 'You may have mistyped the address or the page may have moved'
|
23
|
+
end
|
24
|
+
|
25
|
+
scenario 'api without authorization' do
|
26
|
+
Capybara.current_session.driver.header 'Authorization', "Bearer #{student_api_client.token}"
|
27
|
+
|
28
|
+
visit '/api/nonexistentroute'
|
29
|
+
|
30
|
+
expect(page.text).to json_eq errors: [
|
31
|
+
'Access to organization base' +
|
32
|
+
' was forbidden to user foo+1@bar.com' +
|
33
|
+
' with permissions {"student":"central/*","teacher":"","headmaster":"","janitor":"","owner":""}']
|
34
|
+
end
|
35
|
+
|
36
|
+
scenario 'api with authentication' do
|
37
|
+
Capybara.current_session.driver.header 'Authorization', "Bearer #{owner_api_client.token}"
|
38
|
+
|
39
|
+
visit '/api/nonexistentroute'
|
40
|
+
|
41
|
+
expect(page.text).to json_eq errors: ['not found']
|
42
|
+
end
|
43
|
+
end
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
@@ -57,8 +57,12 @@ Mumukit::Auth.configure do |c|
|
|
57
57
|
c.clients.default = {id: 'test-client', secret: 'thisIsATestSecret'}
|
58
58
|
end
|
59
59
|
|
60
|
-
def
|
61
|
-
@request.env["HTTP_AUTHORIZATION"] =
|
60
|
+
def set_token!(token)
|
61
|
+
@request.env["HTTP_AUTHORIZATION"] = token
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_api_client!(api_client)
|
65
|
+
set_token! api_client.token
|
62
66
|
end
|
63
67
|
|
64
68
|
def reindex_organization!(organization)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mumuki-laboratory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.10.
|
4
|
+
version: 5.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Franco Bulgarelli
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -333,7 +333,6 @@ files:
|
|
333
333
|
- app/controllers/concerns/on_base_organization_only.rb
|
334
334
|
- app/controllers/concerns/organizations_controller_template.rb
|
335
335
|
- app/controllers/concerns/users_controller_template.rb
|
336
|
-
- app/controllers/concerns/with_api_errors.rb
|
337
336
|
- app/controllers/concerns/with_authorization.rb
|
338
337
|
- app/controllers/concerns/with_errors_filter.rb
|
339
338
|
- app/controllers/discussions_controller.rb
|
@@ -960,7 +959,8 @@ files:
|
|
960
959
|
- spec/features/links_flow_spec.rb
|
961
960
|
- spec/features/login_flow_spec.rb
|
962
961
|
- spec/features/menu_bar_spec.rb
|
963
|
-
- spec/features/
|
962
|
+
- spec/features/not_found_private_flow_spec.rb
|
963
|
+
- spec/features/not_found_public_flow_spec.rb
|
964
964
|
- spec/features/profile_flow_spec.rb
|
965
965
|
- spec/features/progressive_tips_spec.rb
|
966
966
|
- spec/features/standard_flow_spec.rb
|
@@ -1148,7 +1148,8 @@ test_files:
|
|
1148
1148
|
- spec/features/links_flow_spec.rb
|
1149
1149
|
- spec/features/login_flow_spec.rb
|
1150
1150
|
- spec/features/menu_bar_spec.rb
|
1151
|
-
- spec/features/
|
1151
|
+
- spec/features/not_found_private_flow_spec.rb
|
1152
|
+
- spec/features/not_found_public_flow_spec.rb
|
1152
1153
|
- spec/features/profile_flow_spec.rb
|
1153
1154
|
- spec/features/progressive_tips_spec.rb
|
1154
1155
|
- spec/features/standard_flow_spec.rb
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module WithApiErrors
|
2
|
-
# TODO we should extend DynamiceErrors
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
unless Rails.application.config.consider_all_requests_local
|
7
|
-
rescue_from ActionController::RoutingError, with: :not_found!
|
8
|
-
end
|
9
|
-
|
10
|
-
rescue_from ActiveRecord::RecordNotFound, with: :not_found!
|
11
|
-
rescue_from Mumukit::Auth::UnauthorizedAccessError, with: :forbidden!
|
12
|
-
rescue_from ActiveRecord::RecordInvalid, with: :bad_record!
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def bad_record!(e)
|
18
|
-
render_errors! e.record.errors, 400
|
19
|
-
end
|
20
|
-
|
21
|
-
def not_found!(e)
|
22
|
-
render_error! e, 404
|
23
|
-
end
|
24
|
-
|
25
|
-
def forbidden!(e)
|
26
|
-
render_error! e, 403
|
27
|
-
end
|
28
|
-
|
29
|
-
def render_error!(e, status)
|
30
|
-
render_errors! [e.message], status
|
31
|
-
end
|
32
|
-
|
33
|
-
def render_errors!(errors, status)
|
34
|
-
summary = { errors: errors }
|
35
|
-
render json: summary, status: status
|
36
|
-
end
|
37
|
-
end
|