[#43387] cannot connect to NC when NC runs in a subpath

https://community.openproject.org/work_packages/43387
pull/11033/head
Wieland Lindenthal 2 years ago
parent f0ff726381
commit b84d3ab1c4
  1. 16
      app/services/oauth_clients/connection_manager.rb
  2. 2
      modules/storages/lib/api/v3/storages/storage_representer.rb
  3. 2
      modules/storages/spec/features/show_file_links_spec.rb
  4. 2
      modules/storages/spec/lib/api/v3/storages/storages_representer_rendering_spec.rb
  5. 2
      modules/storages/spec/requests/api/v3/file_links/file_links_spec.rb
  6. 2
      modules/storages/spec/requests/api/v3/storages/storages_spec.rb
  7. 40
      spec/services/oauth_clients/connection_manager_spec.rb

@ -52,8 +52,9 @@ module OAuthClients
token = get_existing_token
return ServiceResult.success(result: token) if token.present?
# Return a String with a redirect URL to Nextcloud instead of a token
@redirect_url = redirect_to_oauth_authorize(scope:, state:)
# Return the Nextcloud OAuth authorization URI that a user needs to open to grant access and eventually obtain
# a token.
@redirect_url = get_authorization_uri(scope:, state:)
ServiceResult.failure(result: @redirect_url)
end
@ -75,11 +76,11 @@ module OAuthClients
update_oauth_client_token(oauth_client_token, service_result.result)
end
# Redirect to the "authorize" endpoint of the OAuth2 Authorization Server.
# Returns the URI of the "authorize" endpoint of the OAuth2 Authorization Server.
# @param state (OAuth2 RFC) is a nonce referencing a cookie containing the calling page (URL + params) to which to
# return to at the end of the whole flow.
# @param scope (OAuth2 RFC) specifies the resources to access. Nextcloud only has one global scope.
def redirect_to_oauth_authorize(scope: [], state: nil)
# @param scope (OAuth2 RFC) specifies the resources to access. Nextcloud has only one global scope.
def get_authorization_uri(scope: [], state: nil)
client = rack_oauth_client # Configure and start the rack-oauth2 client
client.authorization_uri(scope:, state:)
end
@ -222,6 +223,7 @@ module OAuthClients
oauth_client_scheme = oauth_client_uri.scheme
oauth_client_host = oauth_client_uri.host
oauth_client_port = oauth_client_uri.port
oauth_client_path = oauth_client_uri.path
Rack::OAuth2::Client.new(
identifier: @oauth_client.client_id,
@ -229,8 +231,8 @@ module OAuthClients
scheme: oauth_client_scheme,
host: oauth_client_host,
port: oauth_client_port,
authorization_endpoint: "/apps/oauth2/authorize",
token_endpoint: "/apps/oauth2/api/v1/token"
authorization_endpoint: File.join(oauth_client_path, "/apps/oauth2/authorize"),
token_endpoint: File.join(oauth_client_path, "/apps/oauth2/api/v1/token")
)
end

@ -93,7 +93,7 @@ module API
link :authorize do
next unless @connection_manager.authorization_state == :failed_authorization
{ href: @connection_manager.redirect_to_oauth_authorize, title: 'Authorize' }
{ href: @connection_manager.get_authorization_uri, title: 'Authorize' }
end
def _type

@ -99,7 +99,7 @@ describe 'Showing of file links in work package', with_flag: { storages_module_a
context 'if user is not authorized in Nextcloud' do
before do
allow(connection_manager).to receive(:authorization_state).and_return(:failed_authorization)
allow(connection_manager).to receive(:redirect_to_oauth_authorize).and_return('https://example.com/authorize')
allow(connection_manager).to receive(:get_authorization_uri).and_return('https://example.com/authorize')
end
it 'must show storage information box with login button' do

@ -42,7 +42,7 @@ describe ::API::V3::Storages::StorageRepresenter, 'rendering' do
allow(connection_manager)
.to receive(:authorization_state).and_return(:connected)
allow(connection_manager)
.to receive(:redirect_to_oauth_authorize).and_return('https://example.com/authorize')
.to receive(:get_authorization_uri).and_return('https://example.com/authorize')
end
describe '_links' do

@ -78,7 +78,7 @@ describe 'API v3 file links resource', with_flag: { storages_module_active: true
allow(connection_manager)
.to receive(:authorization_state).and_return(:connected)
allow(connection_manager)
.to receive(:redirect_to_oauth_authorize).and_return('https://example.com/authorize')
.to receive(:get_authorization_uri).and_return('https://example.com/authorize')
# Mock FileLinkSyncService as if Nextcloud would respond positively
allow(::Storages::FileLinkSyncService)

@ -55,7 +55,7 @@ describe 'API v3 storages resource', with_flag: { storages_module_active: true }
end
before do
allow(connection_manager).to receive(:redirect_to_oauth_authorize).and_return(authorize_url)
allow(connection_manager).to receive(:get_authorization_uri).and_return(authorize_url)
allow(connection_manager).to receive(:authorization_state).and_return(:connected)
allow(::OAuthClients::ConnectionManager).to receive(:new).and_return(connection_manager)
project_storage

@ -31,7 +31,7 @@ require 'webmock/rspec'
describe ::OAuthClients::ConnectionManager, type: :model do
let(:user) { create :user }
let(:host) { "http://example.org" }
let(:host) { "https://example.org" }
let(:provider_type) { ::Storages::Storage::PROVIDER_TYPE_NEXTCLOUD }
let(:storage) { create(:storage, provider_type:, host: "#{host}/") }
let(:scope) { [:all] } # OAuth2 resources to access, specific to provider
@ -44,20 +44,32 @@ describe ::OAuthClients::ConnectionManager, type: :model do
let(:oauth_client_token) { create(:oauth_client_token, oauth_client:, user:) }
let(:instance) { described_class.new(user:, oauth_client:) }
# Test the redirect_to_oauth_authorize function that puts together
# the OAuth2 provider URL (Nextcloud) according to RFC specs.
describe '#redirect_to_oauth_authorize' do
# The get_authorization_uri method returns the OAuth2 authorization URI as a string. That URI is the starting point for
# a user to grant OpenProject access to Nextcloud.
describe '#get_authorization_uri' do
let(:scope) { nil }
let(:state) { nil }
subject { instance.redirect_to_oauth_authorize(scope:, state:) }
subject { instance.get_authorization_uri(scope:, state:) }
context 'with empty state and scope' do
it 'returns the redirect URL' do
expect(subject).to be_a String
expect(subject).to include oauth_client.integration.host
expect(subject).not_to include "scope"
expect(subject).not_to include "state"
shared_examples_for 'returns the authorization URI relative to the host' do
it 'returns the authorization URI' do
expect(subject).to be_a String
expect(subject).to include oauth_client.integration.host
expect(subject).not_to include "scope"
expect(subject).not_to include "state"
end
end
context 'when Nextcloud is installed in the server root' do
it_behaves_like 'returns the authorization URI relative to the host'
end
context 'when Nextcloud is installed in a sub-directory' do
let(:host) { "https://example.org/nextcloud" }
it_behaves_like 'returns the authorization URI relative to the host'
end
end
@ -94,7 +106,7 @@ describe ::OAuthClients::ConnectionManager, type: :model do
it 'returns a redirection URL' do
expect(subject.success).to be_falsey
expect(subject.result).to be_a String
# Details of string are tested above in section #redirect_to_oauth_authorize
# Details of string are tested above in section #get_authorization_uri
end
end
@ -125,9 +137,9 @@ describe ::OAuthClients::ConnectionManager, type: :model do
# In the second step the Authorization Server (Nextcloud) redirects
# to a "callback" endpoint on the OAuth2 client (OpenProject):
# http://<openproject>:4200/oauth_clients/8/callback?state=&code=7kRGJ...jG3KZ
# This callback code basically just calls code_to_token(code).
# The callback endpoint calls code_to_token(code) with the code
# https://<openproject-server>/oauth_clients/8/callback?state=&code=7kRGJ...jG3KZ
# This callback code basically just calls `code_to_token(code)`.
# The callback endpoint calls `code_to_token(code)` with the code
# received and exchanges the code for a bearer+refresh token
# using a HTTP request.
describe '#code_to_token' do

Loading…
Cancel
Save