Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f003085
Add Authentication strategy for wikis module
shiroginne May 7, 2026
ff5922f
Merge branch 'dev' into implementation/74542-add-auth-strategy
shiroginne May 7, 2026
b992f85
Add `UserBound` auth strategy
shiroginne May 8, 2026
73b675a
Updated the rest of the code to use new auth
shiroginne May 8, 2026
386c5e4
Added `InternalUser` and `UserBound` auth strategies
shiroginne May 8, 2026
d3ceefa
Updated the specs
shiroginne May 8, 2026
1347f17
Merge branch 'implementation/74542-add-auth-strategy' of https://gith…
shiroginne May 8, 2026
d01bcbe
Merge branch 'dev' into implementation/74542-add-auth-strategy
shiroginne May 8, 2026
2de7642
Merge branch 'dev' into implementation/74542-add-auth-strategy
shiroginne May 8, 2026
417a630
Update app/services/remote_identities/create_service.rb
shiroginne May 11, 2026
d9568f4
Update BearerToken auth for wiki
shiroginne May 11, 2026
2ba2150
Change call chain for the auth strategies
shiroginne May 11, 2026
2dfd02c
Add provider to the strategy contract
shiroginne May 11, 2026
2568842
Merge branch 'implementation/74542-add-auth-strategy' of https://gith…
shiroginne May 11, 2026
336d2a2
Renamed Strategy to AuthStrategy for wiki module
shiroginne May 11, 2026
42294db
Merge branch 'dev' into implementation/74542-add-auth-strategy
shiroginne May 11, 2026
4e22547
Some code cleaning & refactoring
shiroginne May 11, 2026
7bcaeff
Fix syntax issue
shiroginne May 11, 2026
2a89ea5
Update back `integration.extract_origin_user_id`
shiroginne May 11, 2026
1807a2e
Fix specs
shiroginne May 11, 2026
e733ea1
Update PageLinkMetadata and specs
shiroginne May 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(@user)

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#
# 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.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
Expand Down
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 Input
class AuthStrategyContract < DryApplicationContract
AUTH_METHODS = %i[bearer_token internal].to_set.freeze

params do
required(:key).filled(:symbol, included_in?: AUTH_METHODS)
optional(:user).maybe
optional(:provider).maybe
end
end
end
end
end
39 changes: 39 additions & 0 deletions modules/wikis/app/models/wikis/adapters/input/auth_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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::Adapters::Input
AuthStrategy = Data.define(:key, :user, :provider) do
private_class_method :new

def self.build(key:, user: nil, provider: nil, contract: AuthStrategyContract.new)
contract.call(key:, user:, provider:).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
6 changes: 4 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,10 @@ 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_for(user).bind do |auth_strategy|
resolve("queries.user").call(auth_strategy:)
end
end

def authenticate_via_two_way_oauth2?
Expand Down
47 changes: 47 additions & 0 deletions modules/wikis/app/services/wikis/adapters/authentication.rb
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
class Authentication
class << self
# @param strategy [Input::AuthStrategy]
def [](strategy)
case strategy.key
when :bearer_token
AuthenticationStrategies::BearerToken.new(strategy.user, strategy.provider)
when :internal
AuthenticationStrategies::InternalUser.new(strategy.user)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# 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
include Dry::Monads[:result]

def initialize(user, provider)
@user = user
@provider = provider
end

def call(http_options: {}, **)
fetch_user_token.bind do |token|
yield OpenProject.httpx.bearer_auth(token.access_token).with(http_options)
end
end

private

def fetch_user_token
token = OAuthClientToken.for_user_and_client(@user, @provider.oauth_client).first
return Success(token) if token

Failure(Results::Error.new(source: self.class, code: :missing_token))
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,23 @@
#
# 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.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Wikis::Adapters::Input
UserQuery = Data.define(:access_token)
module Wikis
module Adapters
module AuthenticationStrategies
class InternalUser
def initialize(user)
@user = user
end

def call(**)
yield @user
end
end
end
end
end
6 changes: 6 additions & 0 deletions modules/wikis/app/services/wikis/adapters/base_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,11 @@ def success(result)
def failure(code:)
Failure(Results::Error.new(source: self.class, code:))
end

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
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
module Providers
module Internal
module Authentication
class UserBound
def initialize(model:)
@model = model
end

def call(user)
Input::AuthStrategy.build(key: :internal, user:, provider: @model)
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,21 +34,15 @@ 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) }
end
end
end
end
Expand Down
Loading
Loading