Skip to content
2 changes: 1 addition & 1 deletion app/services/remote_identities/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def initialize(user:, integration:, token:, force_update: false)

def call
if @model.new_record? || @force_update
origin_result = @integration.extract_origin_user_id(@token)
origin_result = @integration.extract_origin_user_id(@token.user)

user_id = origin_result.value_or { return ServiceResult.failure(errors: it) }

Expand Down
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 internal].to_set.freeze

params do
required(:key).filled(:symbol, included_in?: AUTH_METHODS)
optional(:user).maybe
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,11 @@
#++

module Wikis::Adapters::Input
UserQuery = Data.define(:access_token)
Strategy = Data.define(:key, :user) do
private_class_method :new

def self.build(key:, user: nil, contract: StrategyContract.new)
contract.call(key:, user:).to_monad.fmap { new(**it.to_h) }
end
end
end
4 changes: 4 additions & 0 deletions modules/wikis/app/models/wikis/provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class Provider < ApplicationRecord
def to_s = self.class.registry_prefix
def user_connected?(_user) = raise SubclassResponsibilityError

def auth_strategy_for(user)
resolve("authentication.user_bound").call(user)
end

class << self
def registry_prefix = raise SubclassResponsibilityError
end
Expand Down
5 changes: 3 additions & 2 deletions modules/wikis/app/models/wikis/xwiki_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ def user_connected?(user)
OAuthClientToken.for_user_and_client(user, oauth_client).exists?
end

def extract_origin_user_id(token)
resolve("queries.user").call(Wikis::Adapters::Input::UserQuery.new(access_token: token.access_token))
def extract_origin_user_id(user)
auth_strategy = auth_strategy_for(user)
resolve("queries.user").call(auth_strategy:)
end

def authenticate_via_two_way_oauth2?
Expand Down
51 changes: 51 additions & 0 deletions modules/wikis/app/services/wikis/adapters/authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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.user)
when :internal
AuthenticationStrategies::InternalUser.new(auth.user)
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,46 @@
# 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(user)
@user = user
end

def call(provider:, http_options: {}, **)
token = OAuthClientToken.for_user_and_client(@user, provider.oauth_client).first
yield OpenProject.httpx.bearer_auth(token&.access_token).with(http_options)
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 InternalUser
def initialize(user)
@user = user
end

def call(**)
yield @user
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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 Providers
module Internal
module Authentication
class UserBound
def initialize(_model:); end

def call(user)
Input::Strategy.build(key: :internal, user:)
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,24 @@ module Providers
module Internal
module Queries
class PageInfo < BaseQuery
def call(input_data)
# TODO: should we accept implicit User.current or do we want to pass in a user explicitly?
wiki_page = WikiPage.visible.find_by(id: input_data.identifier)
return failure(code: :not_found) if wiki_page.nil?
def call(input_data:, auth_strategy:)
Authentication[auth_strategy].call do |user|
wiki_page = WikiPage.visible(user).find_by(id: input_data.identifier)
return failure(code: :not_found) if wiki_page.nil?

success(
Results::PageInfo.new(
identifier: input_data.identifier,
provider:,
title: wiki_page.title,
href: url_for(only_path: true,
controller: "/wiki",
action: "show",
project_id: wiki_page.project.identifier,
id: wiki_page.slug)
success(
Results::PageInfo.new(
identifier: input_data.identifier,
provider:,
title: wiki_page.title,
href: url_for(only_path: true,
controller: "/wiki",
action: "show",
project_id: wiki_page.project.identifier,
id: wiki_page.slug)
)
)
)
end
end

private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,22 @@ module Providers
module Internal
module Queries
class ReferencingPages < BaseQuery
def call(input_data)
def call(input_data:, auth_strategy:)
success(
provider.page_links
.merge(ReverseInlinePageLink.all)
.where(linkable: input_data.linkable)
.order(created_at: :desc)
.map { page_info(provider:, identifier: it.identifier) }
.map { page_info(identifier: it.identifier, auth_strategy:) }
)
end

private

def page_info(provider:, identifier:)
Adapters::Input::PageInfo.build(identifier:).bind { provider.resolve("queries.page_info").call(it) }
def page_info(identifier:, auth_strategy:)
Adapters::Input::PageInfo.build(identifier:).bind do |input|
provider.resolve("queries.page_info").call(input_data: input, auth_strategy:)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,21 @@ module Providers
module Internal
module Queries
class RelationPageLinks < BaseQuery
def call(input_data)
def call(input_data:, auth_strategy:)
page_link_infos = provider.page_links
.merge(RelationPageLink.all)
.where(linkable: input_data.linkable)
.map { |page_link| page_info(page_link.identifier) }
.map { |page_link| page_info(page_link.identifier, auth_strategy:) }

success(page_link_infos)
end

private

def page_info(identifier)
Input::PageInfo.build(identifier:).bind { provider.resolve("queries.page_info").call(it) }
def page_info(identifier, auth_strategy:)
Input::PageInfo.build(identifier:).bind do |input|
provider.resolve("queries.page_info").call(input_data: input, auth_strategy:)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module Providers
module Internal
Registry = Dry::Container::Namespace.new("internal") do
namespace("authentication") do
# ...
register(:user_bound, Authentication::UserBound)
end

namespace("commands") do
Expand Down
Loading
Loading