Skip to content

Commit

Permalink
[#53620] Add OAuth application flow for builtin mobile app
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverguenther authored and Kharonus committed Aug 29, 2024
1 parent acbcb07 commit 9bc91f4
Show file tree
Hide file tree
Showing 34 changed files with 763 additions and 107 deletions.
38 changes: 36 additions & 2 deletions app/components/oauth/applications/row_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,20 @@ def application
model
end

def builtin
checkmark(application.builtin?)
end

def enabled
checkmark(application.enabled?)
end

def name
link_to application.name, oauth_application_path(application)
if enabled
link_to application.name, oauth_application_path(application)
else
render(Primer::Beta::Text.new(color: :muted)) { application.name }
end
end

def owner
Expand Down Expand Up @@ -73,7 +85,29 @@ def edit_link
end

def button_links
[edit_link, helpers.delete_link(oauth_application_path(application))]
if application.builtin?
[toggle_enabled_link]
else
[edit_link, helpers.delete_link(oauth_application_path(application))]
end
end

def toggle_enabled_link
if application.enabled?
link_to(
I18n.t(:button_deactivate),
toggle_oauth_application_path(application),
class: "oauth-application--edit-link icon icon-lock",
method: :post
)
else
link_to(
I18n.t(:button_activate),
toggle_oauth_application_path(application),
class: "oauth-application--edit-link icon icon-unlock",
method: :post
)
end
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/components/oauth/applications/table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def headers
[
["name", { caption: ::Doorkeeper::Application.human_attribute_name(:name) }],
["owner", { caption: ::Doorkeeper::Application.human_attribute_name(:owner) }],
["builtin", { caption: ::Doorkeeper::Application.human_attribute_name(:builtin) }],
["enabled", { caption: ::Doorkeeper::Application.human_attribute_name(:enabled) }],
["client_credentials", { caption: I18n.t("oauth.client_credentials") }],
["redirect_uri", { caption: ::Doorkeeper::Application.human_attribute_name(:redirect_uri) }],
["confidential", { caption: ::Doorkeeper::Application.human_attribute_name(:confidential) }]
Expand Down
2 changes: 1 addition & 1 deletion app/contracts/concerns/requires_admin_guard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module RequiresAdminGuard

# Adds an error if user is archived or not an admin.
def validate_admin_only
unless user.admin? && user.active?
unless user.active_admin?
errors.add :base, :error_unauthorized
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/contracts/delete_contract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def authorized?

case permission
when :admin
user.admin? && user.active?
user.active_admin?
when Proc
instance_exec(&permission)
when Symbol
Expand Down
75 changes: 75 additions & 0 deletions app/contracts/oauth/applications/base_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 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 OAuth
module Applications
class BaseContract < ::ModelContract
def self.model
::Doorkeeper::Application
end

validate :validate_client_credential_user
validate :validate_integration
validate :validate_admin_only

attribute :enabled
attribute :name
attribute :redirect_uri
attribute :confidential
attribute :owner_id
attribute :owner_type
attribute :scopes
attribute :client_credentials_user_id
attribute :integration_id
attribute :integration_type

private

def validate_admin_only
unless user.active_admin?
errors.add :base, :error_unauthorized
end
end

def validate_integration
if (model.integration_id.nil? && model.integration_type.present?) ||
(model.integration_id.present? && model.integration_type.nil?)
errors.add :integration, :invalid
end
end

def validate_client_credential_user
return if model.client_credentials_user_id.blank?

unless User.exists?(id: model.client_credentials_user_id)
errors.add :client_credentials_user_id, :invalid
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -27,39 +27,19 @@
#++

module OAuth
class PersistApplicationService
include Contracted
module Applications
class CreateContract < BaseContract
attribute :builtin
attribute :uid

attr_reader :application, :current_user
validate :validate_owner_present

def initialize(model, user:)
@application = model
@current_user = user
private

self.contract_class = OAuth::ApplicationContract
end

def call(attributes)
set_defaults
application.attributes = attributes
set_secret_and_id

result, errors = validate_and_save(application, current_user)
ServiceResult.new success: result, errors:, result: application
end

def set_defaults
return if application.owner_id

application.owner = current_user
application.owner_type = "User"
end

def set_secret_and_id
application.extend(OpenProject::ChangedBySystem)
application.change_by_system do
application.renew_secret if application.secret.blank?
application.uid = Doorkeeper::OAuth::Helpers::UniqueToken.generate if application.uid.blank?
def validate_owner_present
if model.owner.blank?
errors.add(:owner, :blank)
end
end
end
end
Expand Down
35 changes: 35 additions & 0 deletions app/contracts/oauth/applications/delete_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 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 OAuth
module Applications
class DeleteContract < ::DeleteContract
delete_permission -> { !model.builtin? && user.active_admin? }
end
end
end
34 changes: 34 additions & 0 deletions app/contracts/oauth/applications/update_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 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 OAuth
module Applications
class UpdateContract < BaseContract
end
end
end
2 changes: 1 addition & 1 deletion app/contracts/oauth_clients/create_contract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class CreateContract < ::ModelContract
private

def validate_user_allowed
unless user.admin? && user.active?
unless user.active_admin?
errors.add :base, :error_unauthorized
end
end
Expand Down
34 changes: 24 additions & 10 deletions app/controllers/oauth/applications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
module OAuth
class ApplicationsController < ::ApplicationController
before_action :require_admin
before_action :new_app, only: %i[new create]
before_action :find_app, only: %i[edit update show destroy]
before_action :find_app, only: %i[edit update show toggle destroy]
before_action :prevent_builtin_edits, only: %i[edit update destroy]

layout "admin"
menu_item :oauth_applications
Expand All @@ -44,26 +44,34 @@ def show
flash.delete :reveal_secret
end

def new; end
def new
@application = ::Doorkeeper::Application.new
end

def edit; end

def create
call = ::OAuth::PersistApplicationService.new(@application, user: current_user)
.call(permitted_params.oauth_application)
call = ::OAuth::Applications::CreateService.new(user: current_user)
.call(permitted_params.oauth_application)

if call.success?
flash[:notice] = t(:notice_successful_create)
flash[:_application_secret] = call.result.plaintext_secret
redirect_to action: :show, id: call.result.id
else
@application = call.result
render action: :new
end
end

Check notice on line 65 in app/controllers/oauth/applications_controller.rb

View workflow job for this annotation

GitHub Actions / rubocop

[rubocop] app/controllers/oauth/applications_controller.rb#L53-L65 <Metrics/AbcSize>

Assignment Branch Condition size for create is too high. [<4, 18, 2> 18.55/17]
Raw output
app/controllers/oauth/applications_controller.rb:53:5: C: Metrics/AbcSize: Assignment Branch Condition size for create is too high. [<4, 18, 2> 18.55/17]

def toggle
@application.toggle!(:enabled)
redirect_to action: :index
end

def update
call = ::OAuth::PersistApplicationService.new(@application, user: current_user)
.call(permitted_params.oauth_application)
call = ::OAuth::Applications::UpdateService.new(model: @application, user: current_user)
.call(permitted_params.oauth_application)

if call.success?
flash[:notice] = t(:notice_successful_update)
Expand All @@ -75,7 +83,11 @@ def update
end

def destroy
if @application.destroy
call = OAuth::Applications::DeleteService
.new(model: @application, user: current_user)
.call

if call.success?
flash[:notice] = t(:notice_successful_delete)
else
flash[:error] = t(:error_can_not_delete_entry)
Expand All @@ -94,8 +106,10 @@ def show_local_breadcrumb

private

def new_app
@application = ::Doorkeeper::Application.new
def prevent_builtin_edits
if @application.builtin?
render_403
end
end

def find_app
Expand Down
1 change: 1 addition & 0 deletions app/models/permitted_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ def self.permitted_attributes
:name,
:redirect_uri,
:confidential,
:enabled,
:client_credentials_user_id,
{ scopes: [] }
],
Expand Down
4 changes: 4 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ def wants_comments_in_reverse_order?
pref.comments_in_reverse_order?
end

def active_admin?
admin? && active?
end

def self.find_by_rss_key(key)
return nil unless Setting.feeds_enabled?

Expand Down
Loading

0 comments on commit 9bc91f4

Please sign in to comment.