Skip to content

Commit 7619884

Browse files
authored
Merge pull request #21 from /issues/20-Check_Oauth_user_token_against_Hydra
Check Oauth user token against Hydra
2 parents 4b4fd19 + b0df95d commit 7619884

File tree

12 files changed

+114
-23
lines changed

12 files changed

+114
-23
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ ALLOWED_ORIGINS=localhost:3002,localhost:3000
22
POSTGRES_HOST=changeme
33
POSTGRES_USER=changeme
44
POSTGRES_PASSWORD=changeme
5+
6+
HYDRA_ADMIN_URL=http://docker.for.mac.localhost:9001
7+
HYDRA_SECRET=

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
66
ruby '3.0.3'
77

88
gem 'bootsnap', require: false
9+
gem 'faraday'
910
gem 'importmap-rails'
1011
gem 'jbuilder'
1112
gem 'pg', '~> 1.1'
@@ -32,4 +33,5 @@ end
3233

3334
group :test do
3435
gem 'shoulda-matchers', '~> 5.0'
36+
gem 'webmock'
3537
end

Gemfile.lock

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,17 @@ GEM
6060
i18n (>= 1.6, < 2)
6161
minitest (>= 5.1)
6262
tzinfo (~> 2.0)
63+
addressable (2.8.0)
64+
public_suffix (>= 2.0.2, < 5.0)
6365
ast (2.4.2)
6466
bootsnap (1.9.3)
6567
msgpack (~> 1.0)
6668
builder (3.2.4)
6769
byebug (11.1.3)
6870
coderay (1.1.3)
6971
concurrent-ruby (1.1.9)
72+
crack (0.4.5)
73+
rexml
7074
crass (1.0.6)
7175
diff-lcs (1.4.4)
7276
docile (1.4.0)
@@ -82,8 +86,13 @@ GEM
8286
railties (>= 5.0.0)
8387
faker (2.19.0)
8488
i18n (>= 1.6, < 2)
89+
faraday (2.2.0)
90+
faraday-net_http (~> 2.0)
91+
ruby2_keywords (>= 0.0.4)
92+
faraday-net_http (2.0.1)
8593
globalid (1.0.0)
8694
activesupport (>= 5.0)
95+
hashdiff (1.0.1)
8796
i18n (1.8.11)
8897
concurrent-ruby (~> 1.0)
8998
importmap-rails (1.0.1)
@@ -120,6 +129,7 @@ GEM
120129
pry-byebug (3.9.0)
121130
byebug (~> 11.0)
122131
pry (~> 0.13.0)
132+
public_suffix (4.0.6)
123133
puma (5.5.2)
124134
nio4r (~> 2.0)
125135
racc (1.6.0)
@@ -199,6 +209,7 @@ GEM
199209
rubocop-rspec (2.8.0)
200210
rubocop (~> 1.19)
201211
ruby-progressbar (1.11.0)
212+
ruby2_keywords (0.0.5)
202213
shoulda-matchers (5.0.0)
203214
activesupport (>= 5.2.0)
204215
simplecov (0.21.2)
@@ -223,6 +234,10 @@ GEM
223234
tzinfo (2.0.4)
224235
concurrent-ruby (~> 1.0)
225236
unicode-display_width (2.1.0)
237+
webmock (3.14.0)
238+
addressable (>= 2.8.0)
239+
crack (>= 0.3.2)
240+
hashdiff (>= 0.4.0, < 2.0.0)
226241
websocket-driver (0.7.5)
227242
websocket-extensions (>= 0.1.0)
228243
websocket-extensions (0.1.5)
@@ -238,6 +253,7 @@ DEPENDENCIES
238253
dotenv-rails
239254
factory_bot_rails
240255
faker
256+
faraday
241257
importmap-rails
242258
jbuilder
243259
pg (~> 1.1)
@@ -256,6 +272,7 @@ DEPENDENCIES
256272
sprockets-rails
257273
stimulus-rails
258274
turbo-rails
275+
webmock
259276

260277
RUBY VERSION
261278
ruby 3.0.3p157

app/concepts/project/operation/create_remix.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,29 @@ module Operation
55
class CreateRemix
66
require 'operation_response'
77

8-
def self.call(params)
8+
def self.call(params, user_id)
99
response = OperationResponse.new
1010

11-
validate_params(response, params)
11+
validate_params(response, params, user_id)
1212
return response if response.failure?
1313

14-
remix_project(response, params)
14+
remix_project(response, params, user_id)
1515
response
1616
end
1717

1818
class << self
1919
private
2020

21-
def validate_params(response, params)
22-
valid = params[:phrase_id].present? && params[:remix][:user_id].present?
21+
def validate_params(response, params, user_id)
22+
valid = params[:phrase_id].present? && user_id.present?
2323
response[:error] = 'Invalid parameters' unless valid
2424
end
2525

26-
def remix_project(response, params)
26+
def remix_project(response, params, user_id)
2727
original_project = Project.find_by!(identifier: params[:phrase_id])
2828

2929
response[:project] = original_project.dup.tap do |proj|
30-
proj.user_id = params[:remix][:user_id]
30+
proj.user_id = user_id
3131
proj.components = original_project.components.map(&:dup)
3232
end
3333

app/controllers/api/projects/remixes_controller.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
module Api
44
module Projects
55
class RemixesController < ApiController
6+
before_action :require_oauth_user
7+
68
def create
7-
result = Project::Operation::CreateRemix.call(remix_params)
9+
result = Project::Operation::CreateRemix.call(remix_params, oauth_user_id)
810

911
if result.success?
1012
@project = result[:project]
@@ -17,7 +19,7 @@ def create
1719
private
1820

1921
def remix_params
20-
params.permit(:phrase_id, remix: [:user_id])
22+
params.permit(:phrase_id)
2123
end
2224
end
2325
end

app/controllers/api_controller.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
# frozen_string_literal: true
22

33
class ApiController < ActionController::API
4+
include OauthUser
5+
6+
unless Rails.application.config.consider_all_requests_local
7+
rescue_from ActiveRecord::RecordNotFound, with: -> { return404 }
8+
end
9+
10+
private
11+
12+
def require_oauth_user
13+
head :unauthorized unless oauth_user_id
14+
end
15+
16+
def return404
17+
render json: { error: '404 Not found' }, status: :not_found
18+
end
419
end

app/helpers/oauth_user.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
module OauthUser
4+
def oauth_user_id
5+
@oauth_user_id ||= fetch_oauth_user_id
6+
end
7+
8+
private
9+
10+
def fetch_oauth_user_id
11+
return nil if request.headers['Authorization'].blank?
12+
13+
json = hydra_request
14+
json['sub']
15+
end
16+
17+
def hydra_request
18+
con = Faraday.new ENV.fetch('HYDRA_ADMIN_URL')
19+
res = con.post do |req|
20+
req.url '/oauth2/introspect'
21+
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
22+
req.headers['Authorization'] = "Basic #{ENV.fetch('HYDRA_SECRET')}"
23+
req.body = { token: request.headers['Authorization'] }
24+
end
25+
JSON.parse(res.body)
26+
end
27+
end

config/environments/test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
}
2626

2727
# Show full error reports and disable caching.
28-
config.consider_all_requests_local = true
28+
config.consider_all_requests_local = false
2929
config.action_controller.perform_caching = false
3030
config.cache_store = :null_store
3131

spec/concepts/project/operation/create_remix_spec.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require 'rails_helper'
44

55
RSpec.describe Project::Operation::CreateRemix, type: :unit do
6-
subject(:create_remix) { described_class.call(params) }
6+
subject(:create_remix) { described_class.call(params, user_id) }
77

88
let(:user_id) { 'e0675b6c-dc48-4cd6-8c04-0f7ac05af51a' }
99
let!(:original_project) { create(:project, :with_components) }
@@ -14,7 +14,7 @@
1414

1515
describe '.call' do
1616
context 'when all params valid' do
17-
let(:params) { { phrase_id: original_project.identifier, remix: { user_id: user_id } } }
17+
let(:params) { { phrase_id: original_project.identifier } }
1818

1919
it 'returns success' do
2020
result = create_remix
@@ -53,7 +53,8 @@
5353
end
5454

5555
context 'when user_id is not present' do
56-
let(:params) { { phrase_id: original_project.identifier, remix: { user_id: '' } } }
56+
let(:user_id) { nil }
57+
let(:params) { { phrase_id: original_project.identifier } }
5758

5859
it 'returns failure' do
5960
result = create_remix

spec/rails_helper.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# frozen_string_literal: true
22

3-
# This file is copied to spec/ when you run 'rails generate rspec:install'
43
require 'simplecov'
54
SimpleCov.start 'rails' do
65
enable_coverage :branch
@@ -9,10 +8,13 @@
98
require 'spec_helper'
109
ENV['RAILS_ENV'] ||= 'test'
1110
require File.expand_path('../config/environment', __dir__)
11+
1212
# Prevent database truncation if the environment is production
1313
abort('The Rails environment is running in production mode!') if Rails.env.production?
1414
require 'rspec/rails'
15+
1516
# Add additional requires below this line. Rails is not loaded until this point!
17+
require 'webmock/rspec'
1618

1719
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
1820

@@ -71,6 +73,7 @@
7173
# arbitrary gems may also be filtered via:
7274
# config.filter_gems_from_backtrace("gem name")
7375
config.include PhraseIdentifierMock
76+
config.include OauthUserMock
7477
end
7578

7679
Shoulda::Matchers.configure do |config|

spec/request/projects/remix_spec.rb

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,29 @@
1111
mock_phrase_generation
1212
end
1313

14-
it 'returns expected response' do
15-
post "/api/projects/phrases/#{original_project.identifier}/remix",
16-
params: { remix: { user_id: user_id } }
14+
context 'when auth is correct' do
15+
before do
16+
mock_oauth_user
17+
end
18+
19+
it 'returns success response' do
20+
post "/api/projects/phrases/#{original_project.identifier}/remix"
21+
22+
expect(response.status).to eq(200)
23+
end
24+
25+
it 'returns 404 response if invalid project' do
26+
post '/api/projects/phrases/no-such-project/remix'
1727

18-
expect(response.status).to eq(200)
28+
expect(response.status).to eq(404)
29+
end
1930
end
2031

21-
context 'when request is invalid' do
22-
it 'returns error response' do
23-
post "/api/projects/phrases/#{original_project.identifier}/remix",
24-
params: { remix: { user_id: '' } }
32+
context 'when auth is invalid' do
33+
it 'returns unauthorized' do
34+
post "/api/projects/phrases/#{original_project.identifier}/remix"
2535

26-
expect(response.status).to eq(400)
36+
expect(response.status).to eq(401)
2737
end
2838
end
2939
end

spec/support/oauth_user_mock.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
module OauthUserMock
4+
def mock_oauth_user(user_id = nil)
5+
user_id ||= SecureRandom.uuid
6+
7+
# rubocop:disable RSpec/AnyInstance
8+
allow_any_instance_of(OauthUser).to receive(:oauth_user_id).and_return(user_id)
9+
# rubocop:enable RSpec/AnyInstance
10+
end
11+
end

0 commit comments

Comments
 (0)