Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ Metrics/PerceivedComplexity:
- "app/operations/**/*.rb"
- "app/services/**/*.rb"
- "lib/support/**/*.rb"
- "spec/**/*.rb"

Naming/AccessorMethodName:
Enabled: false
Expand Down
34 changes: 17 additions & 17 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ GEM
with_advisory_lock (>= 7.5.0)
zeitwerk (~> 2.7)
coderay (1.1.3)
concurrent-ruby (1.3.6)
concurrent-ruby (1.3.7)
connection_pool (3.0.2)
console (1.34.3)
fiber-annotation
Expand Down Expand Up @@ -296,13 +296,13 @@ GEM
railties (>= 6.1.0)
faker (3.8.0)
i18n (>= 1.8.11, < 2)
faraday (2.14.1)
faraday (2.14.3)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-follow_redirects (0.5.0)
faraday (>= 1, < 3)
faraday-net_http (3.4.2)
faraday-net_http (3.4.4)
net-http (~> 0.5)
faraday-retry (2.4.0)
faraday (~> 2.0)
Expand Down Expand Up @@ -412,7 +412,7 @@ GEM
jmespath (1.6.2)
job-iteration (1.13.1)
activejob (>= 7.0)
json (2.19.5)
json (2.20.0)
json-jwt (1.17.0)
activesupport (>= 4.2)
aes_key_wrap
Expand All @@ -425,7 +425,7 @@ GEM
hana (~> 1.3)
regexp_parser (~> 2.0)
simpleidn (~> 0.2)
jwt (2.10.2)
jwt (2.10.3)
base64
keycloak-admin (1.1.7)
http-cookie (~> 1.0, >= 1.0.3)
Expand Down Expand Up @@ -507,7 +507,7 @@ GEM
naught (1.1.0)
net-http (0.9.1)
uri (>= 0.11.1)
net-imap (0.6.4)
net-imap (0.6.4.1)
date
net-protocol
net-pop (0.1.2)
Expand All @@ -520,24 +520,24 @@ GEM
newrelic_rpm (10.4.0)
logger
nio4r (2.7.5)
nokogiri (1.19.3)
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri (1.19.3-aarch64-linux-gnu)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.3-aarch64-linux-musl)
nokogiri (1.19.4-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.19.3-arm-linux-gnu)
nokogiri (1.19.4-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.3-arm-linux-musl)
nokogiri (1.19.4-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.19.3-arm64-darwin)
nokogiri (1.19.4-arm64-darwin)
racc (~> 1.4)
nokogiri (1.19.3-x86_64-darwin)
nokogiri (1.19.4-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.19.3-x86_64-linux-gnu)
nokogiri (1.19.4-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.3-x86_64-linux-musl)
nokogiri (1.19.4-x86_64-linux-musl)
racc (~> 1.4)
nom-xml (1.2.0)
i18n
Expand All @@ -547,7 +547,7 @@ GEM
faraday (< 3)
faraday-follow_redirects (>= 0.3.0, < 2)
rexml
oj (3.17.0)
oj (3.17.3)
bigdecimal (>= 3.0)
ostruct (>= 0.2)
openid_connect (2.3.1)
Expand Down Expand Up @@ -860,7 +860,7 @@ GEM
with_advisory_lock (7.5.0)
activerecord (>= 7.2)
zeitwerk (>= 2.7)
yard (0.9.43)
yard (0.9.44)
yard-activerecord (0.0.17)
activesupport
yard (>= 0.8.3)
Expand Down
18 changes: 18 additions & 0 deletions app/graphql/mutations/submission_destroy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Mutations
# @see Mutations::Operations::SubmissionDestroy
class SubmissionDestroy < Mutations::BaseMutation
description <<~TEXT
Destroy a single `Submission` record.
TEXT

argument :submission_id, ID, loads: Types::SubmissionType, required: true do
description <<~TEXT
The submission to destroy.
TEXT
end

performs_operation! "mutations.operations.submission_destroy", destroy: true
end
end
1 change: 1 addition & 0 deletions app/graphql/types/access_grant_subject_type.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

module Types
# @see AccessGrantSubject
module AccessGrantSubjectType
include Types::BaseInterface

Expand Down
18 changes: 10 additions & 8 deletions app/graphql/types/mutation_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ class MutationType < Types::BaseObject

field :apply_schema_properties, mutation: Mutations::ApplySchemaProperties

field :contributor_claim, mutation: Mutations::ContributorClaim

field :contributor_merge, mutation: Mutations::ContributorMerge

field :contributor_user_link_destroy, mutation: Mutations::ContributorUserLinkDestroy

field :contributor_user_link_upsert, mutation: Mutations::ContributorUserLinkUpsert

field :controlled_vocabulary_destroy, mutation: Mutations::ControlledVocabularyDestroy

field :controlled_vocabulary_source_update, mutation: Mutations::ControlledVocabularySourceUpdate
Expand Down Expand Up @@ -128,6 +136,8 @@ class MutationType < Types::BaseObject

field :submission_create, mutation: Mutations::SubmissionCreate

field :submission_destroy, mutation: Mutations::SubmissionDestroy

field :submission_leave_review, mutation: Mutations::SubmissionLeaveReview

field :submission_publish, mutation: Mutations::SubmissionPublish
Expand Down Expand Up @@ -177,13 +187,5 @@ class MutationType < Types::BaseObject
field :upsert_contribution, mutation: Mutations::UpsertContribution

field :user_reset_password, mutation: Mutations::UserResetPassword

field :contributor_user_link_destroy, mutation: Mutations::ContributorUserLinkDestroy

field :contributor_user_link_upsert, mutation: Mutations::ContributorUserLinkUpsert

field :contributor_claim, mutation: Mutations::ContributorClaim

field :contributor_merge, mutation: Mutations::ContributorMerge
end
end
6 changes: 5 additions & 1 deletion app/graphql/types/user_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class UserType < Types::BaseModel
description "Has this user's email been verified to work through Keycloak?"
end

field :email, String, null: true do
field :email, String, null: true, authorize_field: { to: :read_privileged?, raise: false } do
description "A user's email. Depending on the upstream provider, this may not be set."
end

Expand Down Expand Up @@ -111,6 +111,10 @@ class UserType < Types::BaseModel
and that they do not already have a contributor profile linked to their account.
TEXT

expose_authorization_rule :preview?, <<~TEXT
Whether this user has the ability to preview content in the system.
TEXT

expose_authorization_rule :receive_review_requests?, <<~TEXT
Whether this user is a reviewer on **any** submission targets, and should see information about
potential review requests in the UI.
Expand Down
12 changes: 9 additions & 3 deletions app/models/access_grant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,19 @@ def with_allowed_action?(**options)
with_allowed_action(**options).exists?
end

# Constrain to access grants that allow _any_ asset creation.
#
# @see AccessGrantSubject#has_granted_asset_creation?
# @see .with_asset_creation?
# @return [ActiveRecord::Relation<AccessGrant>]
def with_asset_creation
matching_permissions "*.assets.create"
end

def with_asset_creation?
with_asset_creation.exists?
end
# Determine if the current scope has _any_ permission to create assets.
#
# @see .with_asset_creation
# @see AccessGrantSubject#has_granted_asset_creation?
def with_asset_creation? = with_asset_creation.exists?
end
end
2 changes: 2 additions & 0 deletions app/models/application_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ class ApplicationRecord < ActiveRecord::Base
include ModelMutationSupport
include RecordPreloading
include PostgresEnums
include ScopeLocking
include StoreModelIntrospection
include ::Support::CallsCommonOperation
include ::Support::Inspectable
include WhereMatches
include WithAdvisoryLock::Concern

Expand Down
45 changes: 37 additions & 8 deletions app/models/concerns/access_grant_subject.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# frozen_string_literal: true

# This represents something to which an {AccessGrant} can assign a {Role}
# for a given {Accessible} entity, namely a {User}.
# for a given {Accessible} entity, namely a {User}, but intended to allow
# expansion to {UserGroup} in the future.
#
# @see Access::Types::Subject
# @see Types::AccessGrantSubjectType
module AccessGrantSubject
extend ActiveSupport::Concern
extend DefinesMonadicOperation

include AssociationHelpers

Expand All @@ -18,16 +23,40 @@ module AccessGrantSubject
end

# @see Access::AssignGlobalPermissions
# @return [void]
def assign_global_permissions!
call_operation("access.assign_global_permissions", self).value!
# @return [Dry::Monads::Success(void)]
monadic_operation! def assign_global_permissions
call_operation("access.assign_global_permissions", self)
end

def enforce_assignments!
call_operation("access.enforce_assignments", subject: self).value!
# @see Access::EnforceAssignments
# @return [Dry::Monads::Success(void)]
monadic_operation! def enforce_assignments
call_operation("access.enforce_assignments", subject: self)
end

def has_granted_asset_creation?
access_grants.with_asset_creation?
# @param [Hash] options
# @see Access::PolymorphicGrant
# @see Access::PolymorphicGranter
# @return [Dry::Monads::Success(void)]
monadic_operation! def polymorphic_grant(**options)
call_operation("access.polymorphic_grant", self, **options)
end

# @api private
# @note Used in testing to allow for easy granting of permissions
# @param [FactoryBot::Evaluator] evaluator
# @see #polymorphic_grant
# @return [Dry::Monads::Success(void)]
monadic_operation! def polymorphic_grant_from(evaluator)
options = Role::POLYMORPHIC_GRANTABLE_KEYS.index_with do |key|
evaluator.try(key) || []
end

polymorphic_grant(**options)
end

# @note This is used to determine the upload access permission for non-admins.
# @see AccessGrant#with_asset_creation?
# @see User#has_any_upload_access?
def has_granted_asset_creation? = access_grants.with_asset_creation?
end
4 changes: 2 additions & 2 deletions app/models/concerns/checks_contextual_permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def visible_to(user)
# @param [<String>] actions
# @return [ActiveRecord::Relation<HierarchicalEntity>]
def with_permitted_actions_for(user, *actions)
constraint = ContextualSinglePermission.for_hierarchical_type(model_name.to_s).with_permitted_actions_for(user, *actions).select(:hierarchical_id)
constraint = ContextualSinglePermission.with_permitted_actions_for(user, *actions).select(:hierarchical_id)

where(contextual_permission_primary_key => constraint)
end
Expand All @@ -63,7 +63,7 @@ def with_permitted_actions_for(user, *actions)
def arel_visible_to(user)
cppk = arel_table[contextual_permission_primary_key]

permission_constraint = ContextualSinglePermission.for_hierarchical_type(model_name.to_s).with_permitted_actions_for(user, "self.read").select(:hierarchical_id)
permission_constraint = ContextualSinglePermission.with_permitted_actions_for(user, "self.read").select(:hierarchical_id)

has_read_permission = arel_expr_in_query(cppk, permission_constraint)

Expand Down
33 changes: 33 additions & 0 deletions app/models/concerns/scope_locking.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

# A test concern for dealing with locking scopes for GraphQL resolvers.
#
# In the future, we'll test resolvers directly so that this is easier.
module ScopeLocking
extend ActiveSupport::Concern

module ClassMethods
def lock_to!(*records)
records.flatten!

@record_ids, original = records.map(&:id), record_ids

yield
ensure
@record_ids = original
end

# @return [ActiveRecord::Relation]
def maybe_locked
if record_ids.any?
where(id: record_ids)
else
all
end
end

def record_ids
@record_ids ||= Dry::Core::Constants::EMPTY_ARRAY
end
end
end
2 changes: 2 additions & 0 deletions app/models/primary_role_assignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class PrimaryRoleAssignment < ApplicationRecord
include GenericAccessible
include View

self.primary_key = %i[subject_type subject_id]

belongs_to :subject, polymorphic: true, inverse_of: :primary_role_assignment
belongs_to :role, inverse_of: :primary_role_assignments
end
24 changes: 24 additions & 0 deletions app/models/role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ class Role < ApplicationRecord
include HasEphemeralSystemSlug
include TimestampScopes

# A list of polymorphic system roles that can be granted.
#
# @see Access::PolymorphicGrant
# @see Access::PolymorphicGranter
# @return [<Symbol>]
POLYMORPHIC_GRANTABLES = %i[
manager
editor
reviewer
depositor
author
reader
].freeze

# @see Access::PolymorphicGrant
# @see Access::PolymorphicGranter
# @return [{ Symbol => Symbol }]
POLYMORPHIC_GRANTABLE_MAP = POLYMORPHIC_GRANTABLES.index_with { :"#{_1}_on" }.freeze

# @see Access::PolymorphicGrant
# @see Access::PolymorphicGranter
# @return [<Symbol>]
POLYMORPHIC_GRANTABLE_KEYS = POLYMORPHIC_GRANTABLE_MAP.values.freeze

pg_enum! :identifier, as: "role_identifier", prefix: :identified_as
pg_enum! :kind, as: "role_kind", prefix: :for
pg_enum! :primacy, as: "role_primacy", prefix: :has, suffix: :primacy
Expand Down
Loading