-
Notifications
You must be signed in to change notification settings - Fork 3.2k
[74542] Add an authentication strategy for the wikis module #23115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 2 commits
f003085
ff5922f
b992f85
73b675a
386c5e4
d3ceefa
1347f17
d01bcbe
2de7642
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module Wikis | ||
| module Adapters | ||
| module Input | ||
| class StrategyContract < DryApplicationContract | ||
| AUTH_METHODS = %i[bearer_token].to_set.freeze | ||
|
|
||
| params do | ||
| required(:key).filled(:symbol, included_in?: AUTH_METHODS) | ||
| optional(:token).maybe(:string) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,7 +55,10 @@ def user_connected?(user) | |
| end | ||
|
|
||
| def extract_origin_user_id(token) | ||
| resolve("queries.user").call(Wikis::Adapters::Input::UserQuery.new(access_token: token.access_token)) | ||
| auth_strategy = Wikis::Adapters::Registry | ||
| .resolve("xwiki.authentication.user_bound") | ||
| .call(token.access_token) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 This doesn't change a lot for callers yet. The caller still has to obtain an access token from a random database table first to use this authentication strategy. Even worse: Imagine building code for a caller that doesn't know which provider is going to be called. Such a caller also wouldn't know which underlying strategy (here: Bearer token) would be used. So they couldn't possibly know that calling the strategy with a bearer token is the correct thing to do. Every authentication strategy should be able to work from the same interface. For example one thing that could most likely work is to pass a user into the auth strategy and let the auth strategy figure out how to get to the data that it needs based on the user and the provider (the latter would by default be passed through a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair point. The user passed to the strategy is the way to go 👍🏻 |
||
| resolve("queries.user").call(auth_strategy:) | ||
| end | ||
|
|
||
| def authenticate_via_two_way_oauth2? | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module Wikis | ||
| module Adapters | ||
| class Authentication | ||
| class << self | ||
| # @param strategy [Input::Strategy] | ||
| def [](strategy) | ||
| auth = strategy.value_or { raise ArgumentError, "Invalid authentication strategy '#{it.inspect}'" } | ||
|
|
||
| case auth.key | ||
| when :bearer_token | ||
| AuthenticationStrategies::BearerToken.new(auth.token) | ||
| else | ||
| raise ArgumentError, "Unknown authentication scheme: #{auth.key}" | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module Wikis | ||
| module Adapters | ||
| module AuthenticationStrategies | ||
| class BearerToken | ||
| def initialize(token) | ||
| @token = token | ||
| end | ||
|
|
||
| def call(http_options: {}, **) | ||
| yield OpenProject.httpx.bearer_auth(@token).with(http_options) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| require "spec_helper" | ||
| require_module_spec_helper | ||
|
|
||
| RSpec.describe Wikis::Adapters::Authentication do | ||
| describe ".[]" do | ||
| subject(:strategy_object) { described_class[strategy] } | ||
|
|
||
| context "with a bearer_token strategy" do | ||
| let(:strategy) { Wikis::Adapters::Input::Strategy.build(key: :bearer_token, token: "t") } | ||
|
|
||
| it { is_expected.to be_a(Wikis::Adapters::AuthenticationStrategies::BearerToken) } | ||
| end | ||
|
|
||
| context "with a unknown strategy" do | ||
| let(:strategy) { Wikis::Adapters::Input::Strategy.build(key: :unknown) } | ||
|
|
||
| it "raises ArgumentError" do | ||
| expect { strategy_object }.to raise_error(ArgumentError) | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| require "spec_helper" | ||
| require_module_spec_helper | ||
|
|
||
| RSpec.describe Wikis::Adapters::AuthenticationStrategies::BearerToken, :webmock do | ||
| let(:url) { "https://xwiki.local/rest/" } | ||
|
|
||
| subject(:strategy) { described_class.new("test-token") } | ||
|
|
||
| describe "#call" do | ||
| it "yields an http client configured with the bearer token" do | ||
| request_stub = stub_request(:get, url) | ||
| .with(headers: { "Authorization" => "Bearer test-token" }) | ||
| .to_return(status: 200, body: "") | ||
|
|
||
| strategy.call { |http| http.get(url) } | ||
|
|
||
| expect(request_stub).to have_been_requested | ||
| end | ||
|
|
||
| it "forwards http_options to the http client" do | ||
| request_stub = stub_request(:get, url) | ||
| .with(headers: { "Authorization" => "Bearer test-token", "Accept" => "application/json" }) | ||
| .to_return(status: 200, body: "") | ||
|
|
||
| strategy.call(http_options: { headers: { "Accept" => "application/json" } }) { |http| http.get(url) } | ||
|
|
||
| expect(request_stub).to have_been_requested | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| require "spec_helper" | ||
| require_module_spec_helper | ||
|
|
||
| RSpec.describe Wikis::Adapters::Input::Strategy do | ||
| describe ".build" do | ||
| subject(:result) { described_class.build(key:, token:) } | ||
|
|
||
| let(:token) { "some-token" } | ||
|
|
||
| context "with a valid key and token" do | ||
| let(:key) { :bearer_token } | ||
|
|
||
| it { is_expected.to be_success } | ||
| it { is_expected.to have_attributes(value!: have_attributes(key: :bearer_token, token: "some-token")) } | ||
| end | ||
|
|
||
| context "without a token" do | ||
| let(:key) { :bearer_token } | ||
| let(:token) { nil } | ||
|
|
||
| it { is_expected.to be_success } | ||
| it { is_expected.to have_attributes(value!: have_attributes(key: :bearer_token, token: nil)) } | ||
| end | ||
|
|
||
| context "with an unknown key" do | ||
| let(:key) { :unknown } | ||
|
|
||
| it { is_expected.to be_failure } | ||
| end | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 This could also be
self.resolve("authentication.user_bound"), which is the much more likely call pattern for a generic caller.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to the
selfhere is not needed, and rubocop would cut it out 😄