From b69dca3f673fbb8fa024c972044006b181df998f Mon Sep 17 00:00:00 2001 From: Niklas van Schrick Date: Mon, 16 Feb 2026 19:18:50 +0100 Subject: [PATCH 1/3] Restrict metadata to logged in users --- app/graphql/types/application_type.rb | 2 +- app/graphql/types/base_object.rb | 8 +++- app/graphql/types/metadata_type.rb | 3 ++ app/policies/global_policy.rb | 1 + docs/graphql/object/application.md | 2 +- .../graphql/query/application_query_spec.rb | 47 ++++++++++++++----- 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/app/graphql/types/application_type.rb b/app/graphql/types/application_type.rb index 930c8d1c..03099e71 100644 --- a/app/graphql/types/application_type.rb +++ b/app/graphql/types/application_type.rb @@ -4,7 +4,7 @@ module Types class ApplicationType < Types::BaseObject description 'Represents the application instance' - field :metadata, Types::MetadataType, null: false, + field :metadata, Types::MetadataType, null: true, description: 'Metadata about the application' field :settings, Types::ApplicationSettingsType, null: true, diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb index cd4105f4..5251a772 100644 --- a/app/graphql/types/base_object.rb +++ b/app/graphql/types/base_object.rb @@ -81,7 +81,7 @@ def self.authorized?(object, context) return object.instance_variable_get(:@sagittarius_object_authorization_bypass) end - subject = object.try(:declarative_policy_subject) || object + subject = object.try(:declarative_policy_subject) || @declarative_policy_subject.try(:call, object) || object authorize.all? do |ability| Ability.allowed?(context[:current_authentication], ability, subject) @@ -95,6 +95,12 @@ def self.authorize(*args) @authorize_args || (superclass.respond_to?(:authorize) ? superclass.authorize : []) end + def self.declarative_policy_subject(&block) + raise 'Cannot redefine declarative_policy_subject' if @declarative_policy_subject && block + + @declarative_policy_subject = block + end + def current_authentication context[:current_authentication] end diff --git a/app/graphql/types/metadata_type.rb b/app/graphql/types/metadata_type.rb index 63752a74..0b64a635 100644 --- a/app/graphql/types/metadata_type.rb +++ b/app/graphql/types/metadata_type.rb @@ -4,6 +4,9 @@ module Types class MetadataType < Types::BaseObject description 'Application metadata' + authorize :read_metadata + declarative_policy_subject { :global } + field :extensions, [GraphQL::Types::String], null: false, description: 'List of loaded extensions' field :version, GraphQL::Types::String, null: false, description: 'Application version' diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 531542e2..825f3332 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -11,6 +11,7 @@ class GlobalPolicy < BasePolicy enable :read_runtime enable :read_flow_type enable :read_flow_type_setting + enable :read_metadata end rule { admin }.policy do diff --git a/docs/graphql/object/application.md b/docs/graphql/object/application.md index 14750c36..13eb3af7 100644 --- a/docs/graphql/object/application.md +++ b/docs/graphql/object/application.md @@ -9,7 +9,7 @@ Represents the application instance | Name | Type | Description | |------|------|-------------| | `legalNoticeUrl` | [`String`](../scalar/string.md) | URL to the legal notice page | -| `metadata` | [`Metadata!`](../object/metadata.md) | Metadata about the application | +| `metadata` | [`Metadata`](../object/metadata.md) | Metadata about the application | | `privacyUrl` | [`String`](../scalar/string.md) | URL to the privacy policy page | | `settings` | [`ApplicationSettings`](../object/applicationsettings.md) | Global application settings | | `termsAndConditionsUrl` | [`String`](../scalar/string.md) | URL to the terms and conditions page | diff --git a/spec/requests/graphql/query/application_query_spec.rb b/spec/requests/graphql/query/application_query_spec.rb index 4e3d001b..9471521b 100644 --- a/spec/requests/graphql/query/application_query_spec.rb +++ b/spec/requests/graphql/query/application_query_spec.rb @@ -28,7 +28,7 @@ post_graphql(query, current_user: current_user) end - context 'when querying application settings' do + context 'when querying as admin' do let(:current_user) { create(:user, :admin) } it 'returns the application settings' do @@ -39,23 +39,44 @@ expect(settings['organizationCreationRestricted']) .to eq(ApplicationSetting.current['organization_creation_restricted']) end - end - it 'returns null application settings because of permissions' do - settings = graphql_data_at(:application, :settings) - expect(settings).to be_nil - end + it 'returns the application version' do + expect(graphql_data_at(:application, :metadata, :version)).to eq(Sagittarius::Version) + end - it 'returns the application version' do - expect(graphql_data_at(:application, :metadata, :version)).to eq(Sagittarius::Version) + it 'returns the list of active extensions' do + expected_extensions = Sagittarius::Extensions.active.map(&:to_s) + expect(graphql_data_at(:application, :metadata, :extensions)).to match_array(expected_extensions) + end end - it 'returns the list of active extensions' do - expected_extensions = Sagittarius::Extensions.active.map(&:to_s) - expect(graphql_data_at(:application, :metadata, :extensions)).to match_array(expected_extensions) + context 'when querying as user' do + let(:current_user) { create(:user) } + + it 'returns null application settings' do + settings = graphql_data_at(:application, :settings) + expect(settings).to be_nil + end + + it 'returns the application version' do + expect(graphql_data_at(:application, :metadata, :version)).to eq(Sagittarius::Version) + end + + it 'returns the list of active extensions' do + expected_extensions = Sagittarius::Extensions.active.map(&:to_s) + expect(graphql_data_at(:application, :metadata, :extensions)).to match_array(expected_extensions) + end end - it 'does not require authentication' do - expect(graphql_errors).to be_nil + context 'when querying without authentication' do + it 'returns null application settings' do + settings = graphql_data_at(:application, :settings) + expect(settings).to be_nil + end + + it 'return null metadata' do + metadata = graphql_data_at(:application, :metadata) + expect(metadata).to be_nil + end end end From fe9d2d28d23dc54b871135de1a7f6dad1e541627 Mon Sep 17 00:00:00 2001 From: Niklas van Schrick Date: Mon, 16 Feb 2026 19:39:17 +0100 Subject: [PATCH 2/3] Fix urls in application type --- app/graphql/types/application_type.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/graphql/types/application_type.rb b/app/graphql/types/application_type.rb index 03099e71..f6e68b18 100644 --- a/app/graphql/types/application_type.rb +++ b/app/graphql/types/application_type.rb @@ -40,15 +40,15 @@ def settings end def privacy_url - ApplicationSetting.current.privacy_url + ApplicationSetting.current[:privacy_url] end def terms_and_conditions_url - ApplicationSetting.current.terms_and_conditions_url + ApplicationSetting.current[:terms_and_conditions_url] end def legal_notice_url - ApplicationSetting.current.legal_notice_url + ApplicationSetting.current[:legal_notice_url] end end end From 33da5344c0c81941fc99d1fa727b3c359230ab50 Mon Sep 17 00:00:00 2001 From: Niklas van Schrick Date: Mon, 16 Feb 2026 19:39:39 +0100 Subject: [PATCH 3/3] Allow to query application in applicationSettingsUpdate mutation --- app/graphql/mutations/application_settings/update.rb | 10 ++++++++-- docs/graphql/mutation/applicationsettingsupdate.md | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/graphql/mutations/application_settings/update.rb b/app/graphql/mutations/application_settings/update.rb index a6b90405..a3fef767 100644 --- a/app/graphql/mutations/application_settings/update.rb +++ b/app/graphql/mutations/application_settings/update.rb @@ -5,6 +5,8 @@ module ApplicationSettings class Update < BaseMutation description 'Update application settings.' + field :application, Types::ApplicationType, null: true, + description: 'The whole updated application object.' field :application_settings, Types::ApplicationSettingsType, null: true, description: 'The updated application settings.' @@ -31,10 +33,14 @@ class Update < BaseMutation description: 'Set if user registration is enabled.' def resolve(params) - ApplicationSettingsUpdateService.new( + response = ApplicationSettingsUpdateService.new( current_authentication, params - ).execute.to_mutation_response(success_key: :application_settings) + ).execute + + return response.to_mutation_response(success_key: :application_settings) if response.error? + + response.to_mutation_response(success_key: :application_settings).merge({ application: {} }) end end end diff --git a/docs/graphql/mutation/applicationsettingsupdate.md b/docs/graphql/mutation/applicationsettingsupdate.md index 99fa6f11..c84d54fc 100644 --- a/docs/graphql/mutation/applicationsettingsupdate.md +++ b/docs/graphql/mutation/applicationsettingsupdate.md @@ -21,6 +21,7 @@ Update application settings. | Name | Type | Description | |------|------|-------------| +| `application` | [`Application`](../object/application.md) | The whole updated application object. | | `applicationSettings` | [`ApplicationSettings`](../object/applicationsettings.md) | The updated application settings. | | `clientMutationId` | [`String`](../scalar/string.md) | A unique identifier for the client performing the mutation. | | `errors` | [`[Error!]!`](../object/error.md) | Errors encountered during execution of the mutation. |