spree_core 5.2.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae177c6a311a310cbcb6d6faf5b879afc52177f9f6bf4c7063edb5ece85fc755
4
- data.tar.gz: 54794ac79f785c9965c7a1f69fe0c88470fdf751cb789cab9db047b6d9ddbaa4
3
+ metadata.gz: 77f4a7f3643f4b9263ab029e06d7870f5f45397e295f4116a485bc82a280d15f
4
+ data.tar.gz: 482208c5fd5725818a539a4daa4d566bd4c57c1c82595166e6232c91c56b876f
5
5
  SHA512:
6
- metadata.gz: a0f404ec813d9fc4eaabcab6cac7a461aae2671d43a55c5b59b3e53c10fbb442acf6fa01171690999f576f93e90ab77a26f767881f77dbe6f14dd73ec5619689
7
- data.tar.gz: d4bacd845e8c842ab4ee8e394feb1006a4289c85ca7569e017eee6053fe29492dfc30466e413793b1af07b00a2f2575487d8746cf9a9ecdf959292b4cc0e6763
6
+ metadata.gz: 6424ad89c3f5a35cade4d6fc2dc67a74bbf1e0fab459727630c49582f5f94581924b16fba3ce54844d9fddac8cf2f2c2e442c4937f00db98d4276f1e5fb45649
7
+ data.tar.gz: 5dbaa89fd0648e3b35d5f4d7d48ee1066d3bf2da71cae97ee2c9769600380c95054f0aee146f845d4c92910f9b1944bc52155b98508345482ba3bb47224c689f
@@ -1,6 +1,15 @@
1
1
  # Implementation class for Cancan gem. Instead of overriding this class, consider adding new permissions
2
2
  # using the special +register_ability+ method which allows extensions to add their own abilities.
3
3
  #
4
+ # The preferred way to add permissions is now through permission sets. See Spree::PermissionSets::Base
5
+ # for more details on creating custom permission sets.
6
+ #
7
+ # @example Configuring role permissions
8
+ # Spree.permissions.assign(:customer_service, [
9
+ # Spree::PermissionSets::OrderDisplay,
10
+ # Spree::PermissionSets::UserManagement
11
+ # ])
12
+ #
4
13
  # See https://github.com/CanCanCommunity/cancancan for more details.
5
14
  require 'cancan'
6
15
 
@@ -11,6 +20,12 @@ module Spree
11
20
  class_attribute :abilities
12
21
  self.abilities = Set.new
13
22
 
23
+ # @return [Object] the current user
24
+ attr_reader :user
25
+
26
+ # @return [Spree::Store, nil] the current store
27
+ attr_reader :store
28
+
14
29
  # Allows us to go beyond the standard cancan initialize method which makes it difficult for engines to
15
30
  # modify the default +Ability+ of an application. The +ability+ argument must be a class that includes
16
31
  # the +CanCan::Ability+ module. The registered ability should behave properly as a stand-alone class
@@ -26,22 +41,16 @@ module Spree
26
41
  def initialize(user, options = {})
27
42
  alias_cancan_delete_action
28
43
 
29
- user ||= Spree.user_class.new
30
- store ||= options[:store] || Spree::Current.store
44
+ @user = user || Spree.user_class.new
45
+ @store = options[:store] || Spree::Current.store
31
46
 
32
- if user.persisted? && user.is_a?(Spree.admin_user_class) && user.try(:spree_admin?, store)
33
- apply_admin_permissions(user, options)
34
- else
35
- apply_user_permissions(user, options)
36
- end
47
+ apply_permissions_from_sets
37
48
 
38
49
  # Include any abilities registered by extensions, etc.
39
50
  # this is legacy behaviour and should be removed in Spree 5.0
40
51
  Ability.abilities.merge(abilities_to_register).each do |clazz|
41
- merge clazz.new(user)
52
+ merge clazz.new(@user)
42
53
  end
43
-
44
- protect_admin_role
45
54
  end
46
55
 
47
56
  protected
@@ -57,6 +66,62 @@ module Spree
57
66
  alias_action :create, :update, :destroy, to: :modify
58
67
  end
59
68
 
69
+ # Applies permissions based on the user's roles and the configured permission sets.
70
+ def apply_permissions_from_sets
71
+ role_names = determine_role_names
72
+ permission_sets = Spree.permissions.permission_sets_for_roles(role_names)
73
+
74
+ # If no permission sets are configured for the user's roles, use legacy behavior
75
+ if permission_sets.empty?
76
+ apply_legacy_permissions
77
+ else
78
+ activate_permission_sets(permission_sets)
79
+ end
80
+ end
81
+
82
+ # Determines the role names for the current user.
83
+ #
84
+ # @return [Array<Symbol>] the role names
85
+ def determine_role_names
86
+ return [:default] unless @user.persisted?
87
+
88
+ # First, try to get roles from the spree_roles association
89
+ if @user.respond_to?(:spree_roles)
90
+ role_names = @user.spree_roles.pluck(:name).map(&:to_sym)
91
+ return role_names if role_names.any?
92
+ end
93
+
94
+ # Fall back to checking spree_admin? for backward compatibility
95
+ # This supports cases where roles are mocked or admin status is determined differently
96
+ if @user.try(:spree_admin?, @store)
97
+ [:admin]
98
+ else
99
+ [:default]
100
+ end
101
+ end
102
+
103
+ # Activates the given permission sets.
104
+ #
105
+ # @param permission_sets [Array<Class>] the permission set classes to activate
106
+ def activate_permission_sets(permission_sets)
107
+ permission_sets.each do |permission_set_class|
108
+ permission_set = permission_set_class.new(self)
109
+ permission_set.activate!
110
+ end
111
+ end
112
+
113
+ # Legacy permission application for backward compatibility.
114
+ # This is used when no permission sets are configured for the user's roles.
115
+ def apply_legacy_permissions
116
+ if @user.persisted? && @user.is_a?(Spree.admin_user_class) && @user.try(:spree_admin?, @store)
117
+ apply_admin_permissions(@user, { store: @store })
118
+ else
119
+ apply_user_permissions(@user, { store: @store })
120
+ end
121
+
122
+ protect_admin_role
123
+ end
124
+
60
125
  def apply_admin_permissions(_user, _options)
61
126
  can :manage, :all
62
127
  cannot :cancel, Spree::Order
@@ -0,0 +1,81 @@
1
+ # Base class for all permission sets.
2
+ #
3
+ # Permission sets are reusable groups of permissions that can be assigned to roles.
4
+ # They provide a clean abstraction over CanCanCan abilities, making it easier to
5
+ # manage permissions in a modular way.
6
+ #
7
+ # @example Creating a custom permission set
8
+ # class Spree::PermissionSets::InventoryManagement < Spree::PermissionSets::Base
9
+ # def activate!
10
+ # can :manage, Spree::StockItem
11
+ # can :manage, Spree::StockLocation
12
+ # can :manage, Spree::StockMovement
13
+ # end
14
+ # end
15
+ #
16
+ # @example Assigning the permission set to a role
17
+ # Spree.permissions.assign(:warehouse_manager, Spree::PermissionSets::InventoryManagement)
18
+ #
19
+ module Spree
20
+ module PermissionSets
21
+ class Base
22
+ # @return [CanCan::Ability] the ability instance to add permissions to
23
+ attr_reader :ability
24
+
25
+ # @param ability [CanCan::Ability] the ability instance to add permissions to
26
+ def initialize(ability)
27
+ @ability = ability
28
+ end
29
+
30
+ # Activates this permission set by adding its permissions to the ability.
31
+ # Override this method in subclasses to define the permissions.
32
+ #
33
+ # @abstract
34
+ # @return [void]
35
+ def activate!
36
+ raise NotImplementedError, "#{self.class} must implement #activate!"
37
+ end
38
+
39
+ protected
40
+
41
+ # Delegates the `can` method to the ability instance.
42
+ #
43
+ # @param args [Array] arguments to pass to CanCan::Ability#can
44
+ # @param block [Proc] optional block for conditional permissions
45
+ def can(*args, &block)
46
+ ability.can(*args, &block)
47
+ end
48
+
49
+ # Delegates the `cannot` method to the ability instance.
50
+ #
51
+ # @param args [Array] arguments to pass to CanCan::Ability#cannot
52
+ # @param block [Proc] optional block for conditional permissions
53
+ def cannot(*args, &block)
54
+ ability.cannot(*args, &block)
55
+ end
56
+
57
+ # Delegates the `can?` method to the ability instance.
58
+ #
59
+ # @param args [Array] arguments to pass to CanCan::Ability#can?
60
+ # @return [Boolean]
61
+ def can?(*args)
62
+ ability.can?(*args)
63
+ end
64
+
65
+ # Returns the user from the ability instance.
66
+ # This method assumes the ability has a user accessor.
67
+ #
68
+ # @return [Object] the current user
69
+ def user
70
+ ability.user
71
+ end
72
+
73
+ # Returns the store from the ability instance options.
74
+ #
75
+ # @return [Spree::Store, nil] the current store
76
+ def store
77
+ ability.store
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,47 @@
1
+ # Permission set for managing store configuration and settings.
2
+ #
3
+ # This permission set provides access to manage store settings,
4
+ # payment methods, shipping methods, and other configuration.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:store_admin, Spree::PermissionSets::ConfigurationManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class ConfigurationManagement < Base
12
+ def activate!
13
+ # Store settings
14
+ can :manage, Spree::Store
15
+
16
+ # Payment configuration
17
+ can :manage, Spree::PaymentMethod
18
+ can :manage, Spree::Gateway
19
+
20
+ # Shipping configuration
21
+ can :manage, Spree::ShippingMethod
22
+ can :manage, Spree::ShippingCategory
23
+ can :manage, Spree::Zone
24
+ can :manage, Spree::ZoneMember
25
+
26
+ # Tax configuration
27
+ can :manage, Spree::TaxCategory
28
+ can :manage, Spree::TaxRate
29
+
30
+ # General configuration
31
+ can :manage, Spree::RefundReason
32
+ can :manage, Spree::ReimbursementType
33
+ can :manage, Spree::ReturnReason
34
+
35
+ # Restrictions on immutable types
36
+ cannot [:edit, :update], Spree::RefundReason, mutable: false
37
+ cannot [:edit, :update], Spree::ReimbursementType, mutable: false
38
+
39
+ # Metafield configuration
40
+ can :manage, Spree::MetafieldDefinition
41
+
42
+ # Policies
43
+ can :manage, Spree::Policy
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ # Permission set for viewing the admin dashboard.
2
+ #
3
+ # This permission set provides access to view the admin dashboard
4
+ # and basic admin navigation.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:viewer, Spree::PermissionSets::DashboardDisplay)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class DashboardDisplay < Base
12
+ def activate!
13
+ can [:admin, :index, :show], :dashboard
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,66 @@
1
+ # Permission set for default storefront customers (both authenticated and guests).
2
+ #
3
+ # This permission set provides the standard permissions needed for browsing
4
+ # the store and making purchases.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:default, Spree::PermissionSets::DefaultCustomer)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class DefaultCustomer < Base
12
+ def activate!
13
+ # Read-only access to catalog
14
+ can :read, Spree::Country
15
+ can :read, Spree::OptionType
16
+ can :read, Spree::OptionValue
17
+ can :read, Spree::Product
18
+ can :read, Spree::ProductProperty
19
+ can :read, Spree::Property
20
+ can :read, Spree::State
21
+ can :read, Spree::Store
22
+ can :read, Spree::Taxon
23
+ can :read, Spree::Taxonomy
24
+ can :read, Spree::Variant
25
+ can :read, Spree::Zone
26
+
27
+ # Content pages
28
+ can :read, Spree::Policy
29
+ can :read, Spree::Page
30
+ can :read, Spree::Post
31
+ can :read, Spree::PostCategory
32
+
33
+ # Order management for the user's own orders
34
+ can :create, Spree::Order
35
+ can :show, Spree::Order do |order, token|
36
+ order.user == user || order.token && token == order.token
37
+ end
38
+ can :update, Spree::Order do |order, token|
39
+ !order.completed? && (order.user == user || order.token && token == order.token)
40
+ end
41
+
42
+ # User account management - available to all users (including guests for their own record)
43
+ can :create, Spree.user_class
44
+ can [:show, :update, :destroy], Spree.user_class, id: user.id
45
+
46
+ # Address management
47
+ can :manage, Spree::Address, user_id: user.id
48
+
49
+ # Credit card management
50
+ can [:read, :destroy], Spree::CreditCard, user_id: user.id
51
+
52
+ # Wishlist management
53
+ can :manage, Spree::Wishlist, user_id: user.id
54
+ can :show, Spree::Wishlist do |wishlist|
55
+ wishlist.user == user || wishlist.is_private == false
56
+ end
57
+ can [:create, :update, :destroy], Spree::WishedItem do |wished_item|
58
+ wished_item.wishlist.user == user
59
+ end
60
+
61
+ # Invitation acceptance
62
+ can :accept, Spree::Invitation, invitee_id: [user.id, nil], invitee_type: user.class.name, status: 'pending'
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ # Permission set for viewing orders and related resources.
2
+ #
3
+ # This permission set provides read-only access to orders and associated
4
+ # models like payments, shipments, and refunds.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:customer_service, Spree::PermissionSets::OrderDisplay)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class OrderDisplay < Base
12
+ def activate!
13
+ can [:read, :admin, :index], Spree::Order
14
+ can [:read, :admin], Spree::Payment
15
+ can [:read, :admin], Spree::Shipment
16
+ can [:read, :admin], Spree::Adjustment
17
+ can [:read, :admin], Spree::LineItem
18
+ can [:read, :admin], Spree::ReturnAuthorization
19
+ can [:read, :admin], Spree::CustomerReturn
20
+ can [:read, :admin], Spree::Reimbursement
21
+ can [:read, :admin], Spree::Refund
22
+ can [:read, :admin], Spree::StoreCredit
23
+ can [:read, :admin], Spree::GiftCard
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # Permission set for full order management.
2
+ #
3
+ # This permission set provides complete access to manage orders,
4
+ # including creating, updating, and processing payments and shipments.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:order_manager, Spree::PermissionSets::OrderManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class OrderManagement < Base
12
+ def activate!
13
+ can :manage, Spree::Order
14
+ can :manage, Spree::Payment
15
+ can :manage, Spree::Shipment
16
+ can :manage, Spree::Adjustment
17
+ can :manage, Spree::LineItem
18
+ can :manage, Spree::ReturnAuthorization
19
+ can :manage, Spree::CustomerReturn
20
+ can :manage, Spree::Reimbursement
21
+ can :manage, Spree::Refund
22
+ can :manage, Spree::StoreCredit
23
+ can :manage, Spree::GiftCard
24
+
25
+ # Order-specific restrictions
26
+ cannot :cancel, Spree::Order
27
+ can :cancel, Spree::Order, &:allow_cancel?
28
+ cannot :destroy, Spree::Order
29
+ can :destroy, Spree::Order, &:can_be_deleted?
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # Permission set for viewing products and catalog information.
2
+ #
3
+ # This permission set provides read-only access to products, variants,
4
+ # and related catalog models.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:content_editor, Spree::PermissionSets::ProductDisplay)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class ProductDisplay < Base
12
+ def activate!
13
+ can [:read, :admin, :index], Spree::Product
14
+ can [:read, :admin], Spree::Variant
15
+ can [:read, :admin], Spree::OptionType
16
+ can [:read, :admin], Spree::OptionValue
17
+ can [:read, :admin], Spree::Property
18
+ can [:read, :admin], Spree::ProductProperty
19
+ can [:read, :admin], Spree::Metafield
20
+ can [:read, :admin], Spree::Taxon
21
+ can [:read, :admin], Spree::Taxonomy
22
+ can [:read, :admin], Spree::Classification
23
+ can [:read, :admin], Spree::Price
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # Permission set for full product and catalog management.
2
+ #
3
+ # This permission set provides complete access to manage products, variants,
4
+ # and related catalog models like taxonomies and properties.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:merchandiser, Spree::PermissionSets::ProductManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class ProductManagement < Base
12
+ def activate!
13
+ can :manage, Spree::Product
14
+ can :manage, Spree::Variant
15
+ can :manage, Spree::OptionType
16
+ can :manage, Spree::OptionValue
17
+ can :manage, Spree::Property
18
+ can :manage, Spree::ProductProperty
19
+ can :manage, Spree::Taxon
20
+ can :manage, Spree::Taxonomy
21
+ can :manage, Spree::Classification
22
+ can :manage, Spree::Price
23
+ can :manage, Spree::Asset
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # Permission set for managing promotions and discounts.
2
+ #
3
+ # This permission set provides access to create and manage promotions,
4
+ # coupon codes, and promotion rules.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:marketing, Spree::PermissionSets::PromotionManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class PromotionManagement < Base
12
+ def activate!
13
+ can :manage, Spree::Promotion
14
+ can :manage, Spree::PromotionRule
15
+ can :manage, Spree::PromotionAction
16
+ can :manage, Spree::PromotionCategory
17
+ can :manage, Spree::CouponCode
18
+ can [:read, :admin], Spree::Metafield
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # Permission set for managing roles and permissions.
2
+ #
3
+ # This permission set provides access to manage roles and user assignments.
4
+ # Note: The admin role cannot be modified.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:admin, Spree::PermissionSets::RoleManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class RoleManagement < Base
12
+ def activate!
13
+ can :manage, Spree::Role
14
+ can :manage, Spree::RoleUser
15
+
16
+ # Protect the admin role from modification
17
+ cannot [:update, :destroy], Spree::Role, name: ['admin']
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # Permission set for viewing stock and inventory information.
2
+ #
3
+ # This permission set provides read-only access to stock items,
4
+ # locations, and movements.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:warehouse_viewer, Spree::PermissionSets::StockDisplay)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class StockDisplay < Base
12
+ def activate!
13
+ can [:read, :admin, :index], Spree::StockItem
14
+ can [:read, :admin], Spree::StockLocation
15
+ can [:read, :admin], Spree::StockMovement
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # Permission set for full stock and inventory management.
2
+ #
3
+ # This permission set provides complete access to manage stock items,
4
+ # locations, and movements.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:warehouse_manager, Spree::PermissionSets::StockManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class StockManagement < Base
12
+ def activate!
13
+ can :manage, Spree::StockItem
14
+ can :manage, Spree::StockLocation
15
+ can :manage, Spree::StockMovement
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # Permission set granting full administrative access.
2
+ #
3
+ # This permission set provides unrestricted access to all resources,
4
+ # with some safety restrictions for critical operations.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:admin, Spree::PermissionSets::SuperUser)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class SuperUser < Base
12
+ def activate!
13
+ can :manage, :all
14
+
15
+ # Safety restrictions
16
+ cannot :cancel, Spree::Order
17
+ can :cancel, Spree::Order, &:allow_cancel?
18
+ cannot :destroy, Spree::Order
19
+ can :destroy, Spree::Order, &:can_be_deleted?
20
+ cannot [:edit, :update], Spree::RefundReason, mutable: false
21
+ cannot [:edit, :update], Spree::ReimbursementType, mutable: false
22
+
23
+ # Protect the admin role from modification
24
+ cannot [:update, :destroy], Spree::Role, name: ['admin']
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ # Permission set for viewing users and related information.
2
+ #
3
+ # This permission set provides read-only access to user accounts,
4
+ # addresses, and credit cards.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:support_staff, Spree::PermissionSets::UserDisplay)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class UserDisplay < Base
12
+ def activate!
13
+ can [:read, :admin, :index], Spree.user_class
14
+ can [:read, :admin], Spree::Address
15
+ can [:read, :admin], Spree::CreditCard
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # Permission set for full user management.
2
+ #
3
+ # This permission set provides complete access to manage user accounts,
4
+ # addresses, and credit cards.
5
+ #
6
+ # @example
7
+ # Spree.permissions.assign(:customer_service, Spree::PermissionSets::UserManagement)
8
+ #
9
+ module Spree
10
+ module PermissionSets
11
+ class UserManagement < Base
12
+ def activate!
13
+ can :manage, Spree.user_class
14
+ can :manage, Spree::Address
15
+ can :manage, Spree::CreditCard
16
+ can [:read, :admin], Spree::Metafield
17
+ end
18
+ end
19
+ end
20
+ end
@@ -624,6 +624,7 @@ en:
624
624
  add_new_style: Add New Style
625
625
  add_one: Add One
626
626
  add_option_value: Add Option Value
627
+ add_page_block: Add Block
627
628
  add_product: Add Product
628
629
  add_product_properties: Add Product Properties
629
630
  add_products: Add Products
@@ -0,0 +1,26 @@
1
+ module FriendlyId
2
+ module HistoryDecorator
3
+ private
4
+
5
+ # This a patch to friendly_id history module.
6
+ # Originally, it removes and re-creates a slug record if it already exists. The issue is that we use `acts_as_paranoid`
7
+ # for slugs so it sets the `deleted_at` timestamp instead of deleting the record. We need to delete the record instead.
8
+ def create_slug
9
+ return unless friendly_id
10
+ return if history_is_up_to_date?
11
+ # Allow reversion back to a previously used slug
12
+ relation = slugs.where(slug: friendly_id)
13
+ if friendly_id_config.uses?(:scoped)
14
+ relation = relation.where(scope: serialized_scope)
15
+ end
16
+ # Use `delete_all` instead of `destroy_all` to avoid the unique index error.
17
+ relation.delete_all unless relation.empty?
18
+ slugs.create! do |record|
19
+ record.slug = friendly_id
20
+ record.scope = serialized_scope if friendly_id_config.uses?(:scoped)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ FriendlyId::History.prepend(FriendlyId::HistoryDecorator)
@@ -95,6 +95,31 @@ Rails.application.config.after_initialize do
95
95
 
96
96
  # Admin partials
97
97
  # Spree.admin.partials.product_form << 'spree/admin/products/custom_section'
98
+
99
+ # Role-based permissions
100
+ # Configure which permission sets are assigned to each role
101
+ # More on permission sets: https://spreecommerce.org/docs/developer/customization/permissions
102
+ Spree.permissions.assign(:default, [Spree::PermissionSets::DefaultCustomer])
103
+ Spree.permissions.assign(:admin, [Spree::PermissionSets::SuperUser])
104
+
105
+ # Example: Create a custom role with specific permissions
106
+ # Spree.permissions.assign(:customer_service, [
107
+ # Spree::PermissionSets::DashboardDisplay,
108
+ # Spree::PermissionSets::OrderManagement,
109
+ # Spree::PermissionSets::UserDisplay
110
+ # ])
111
+ #
112
+ # Available permission sets:
113
+ # - Spree::PermissionSets::SuperUser (full admin access)
114
+ # - Spree::PermissionSets::DefaultCustomer (storefront access)
115
+ # - Spree::PermissionSets::DashboardDisplay (view admin dashboard)
116
+ # - Spree::PermissionSets::OrderDisplay / OrderManagement
117
+ # - Spree::PermissionSets::ProductDisplay / ProductManagement
118
+ # - Spree::PermissionSets::UserDisplay / UserManagement
119
+ # - Spree::PermissionSets::StockDisplay / StockManagement
120
+ # - Spree::PermissionSets::PromotionManagement
121
+ # - Spree::PermissionSets::ConfigurationManagement
122
+ # - Spree::PermissionSets::RoleManagement
98
123
  end
99
124
 
100
125
  Spree.user_class = <%= (options[:user_class].blank? ? 'Spree::LegacyUser' : options[:user_class]).inspect %>
@@ -0,0 +1,102 @@
1
+ # Manages the mapping between roles and permission sets.
2
+ #
3
+ # This configuration allows you to define which permission sets are assigned to each role.
4
+ # Permission sets are reusable groups of permissions that can be applied to roles.
5
+ #
6
+ # @example Assigning permission sets to a role
7
+ # Spree.permissions.assign(:customer_service, [
8
+ # Spree::PermissionSets::OrderDisplay,
9
+ # Spree::PermissionSets::UserManagement
10
+ # ])
11
+ #
12
+ # @example Clearing permission sets from a role
13
+ # Spree.permissions.clear(:customer_service)
14
+ #
15
+ # @example Getting permission sets for a role
16
+ # Spree.permissions.permission_sets_for(:admin)
17
+ # # => [Spree::PermissionSets::SuperUser]
18
+ #
19
+ module Spree
20
+ class PermissionConfiguration
21
+ # Default role used for unauthenticated users
22
+ DEFAULT_ROLE = :default
23
+
24
+ # Admin role with full access
25
+ ADMIN_ROLE = :admin
26
+
27
+ def initialize
28
+ @role_permissions = {}
29
+ end
30
+
31
+ # Assigns permission sets to a role.
32
+ #
33
+ # @param role_name [Symbol, String] the name of the role
34
+ # @param permission_sets [Array<Class>, Class] permission set class(es) to assign
35
+ # @return [Array<Class>] the assigned permission sets
36
+ #
37
+ # @example
38
+ # Spree.permissions.assign(:customer_service, Spree::PermissionSets::OrderDisplay)
39
+ # Spree.permissions.assign(:admin, [
40
+ # Spree::PermissionSets::SuperUser
41
+ # ])
42
+ def assign(role_name, permission_sets)
43
+ role_key = normalize_role_name(role_name)
44
+ @role_permissions[role_key] ||= []
45
+ @role_permissions[role_key] |= Array(permission_sets)
46
+ end
47
+
48
+ # Clears all permission sets from a role.
49
+ #
50
+ # @param role_name [Symbol, String] the name of the role
51
+ # @return [Array<Class>] the removed permission sets
52
+ def clear(role_name)
53
+ role_key = normalize_role_name(role_name)
54
+ @role_permissions.delete(role_key)
55
+ end
56
+
57
+ # Returns the permission sets assigned to a role.
58
+ #
59
+ # @param role_name [Symbol, String] the name of the role
60
+ # @return [Array<Class>] the assigned permission sets
61
+ def permission_sets_for(role_name)
62
+ role_key = normalize_role_name(role_name)
63
+ @role_permissions[role_key] || []
64
+ end
65
+
66
+ # Returns all permission sets for multiple roles.
67
+ #
68
+ # @param role_names [Array<Symbol, String>] the names of the roles
69
+ # @return [Array<Class>] the combined permission sets (deduplicated)
70
+ def permission_sets_for_roles(role_names)
71
+ role_names.flat_map { |role_name| permission_sets_for(role_name) }.uniq
72
+ end
73
+
74
+ # Returns all configured roles.
75
+ #
76
+ # @return [Array<Symbol>] the configured role names
77
+ def roles
78
+ @role_permissions.keys
79
+ end
80
+
81
+ # Checks if a role has any permission sets assigned.
82
+ #
83
+ # @param role_name [Symbol, String] the name of the role
84
+ # @return [Boolean]
85
+ def role_configured?(role_name)
86
+ role_key = normalize_role_name(role_name)
87
+ @role_permissions.key?(role_key) && @role_permissions[role_key].any?
88
+ end
89
+
90
+ # Resets all role permissions to empty state.
91
+ # Useful for testing.
92
+ def reset!
93
+ @role_permissions = {}
94
+ end
95
+
96
+ private
97
+
98
+ def normalize_role_name(role_name)
99
+ role_name.to_s.downcase.to_sym
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '5.2.0'.freeze
2
+ VERSION = '5.2.1'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/spree/core.rb CHANGED
@@ -358,6 +358,22 @@ module Spree
358
358
  end
359
359
  end
360
360
 
361
+ # Permission configuration accessor for managing role-to-permission-set mappings.
362
+ #
363
+ # @example Assigning permission sets to a role
364
+ # Spree.permissions.assign(:customer_service, [
365
+ # Spree::PermissionSets::OrderDisplay,
366
+ # Spree::PermissionSets::UserManagement
367
+ # ])
368
+ #
369
+ # @example Clearing permission sets from a role
370
+ # Spree.permissions.clear(:customer_service)
371
+ #
372
+ # @return [Spree::PermissionConfiguration] the permission configuration instance
373
+ def self.permissions
374
+ @permissions ||= PermissionConfiguration.new
375
+ end
376
+
361
377
  module Core
362
378
  autoload :ProductFilters, 'spree/core/product_filters'
363
379
  autoload :TokenGenerator, 'spree/core/token_generator'
@@ -400,3 +416,4 @@ require 'spree/core/preferences/scoped_store'
400
416
  require 'spree/core/preferences/runtime_configuration'
401
417
 
402
418
  require 'spree/core/webhooks'
419
+ require 'spree/core/permission_configuration'
data/lib/spree_core.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'friendly_id/paranoia'
2
+ require 'friendly_id/history_decorator'
2
3
  require 'mobility/plugins/store_based_fallbacks'
3
4
  require 'normalize_string'
4
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Schofield
@@ -335,28 +335,28 @@ dependencies:
335
335
  requirements:
336
336
  - - "~>"
337
337
  - !ruby/object:Gem::Version
338
- version: '0.10'
338
+ version: '0.100'
339
339
  type: :runtime
340
340
  prerelease: false
341
341
  version_requirements: !ruby/object:Gem::Requirement
342
342
  requirements:
343
343
  - - "~>"
344
344
  - !ruby/object:Gem::Version
345
- version: '0.10'
345
+ version: '0.100'
346
346
  - !ruby/object:Gem::Dependency
347
347
  name: state_machines-activemodel
348
348
  requirement: !ruby/object:Gem::Requirement
349
349
  requirements:
350
350
  - - "~>"
351
351
  - !ruby/object:Gem::Version
352
- version: '0.10'
352
+ version: '0.100'
353
353
  type: :runtime
354
354
  prerelease: false
355
355
  version_requirements: !ruby/object:Gem::Requirement
356
356
  requirements:
357
357
  - - "~>"
358
358
  - !ruby/object:Gem::Version
359
- version: '0.10'
359
+ version: '0.100'
360
360
  - !ruby/object:Gem::Dependency
361
361
  name: stringex
362
362
  requirement: !ruby/object:Gem::Requirement
@@ -1050,6 +1050,21 @@ files:
1050
1050
  - app/models/spree/payment_method/check.rb
1051
1051
  - app/models/spree/payment_method/store_credit.rb
1052
1052
  - app/models/spree/payment_source.rb
1053
+ - app/models/spree/permission_sets/base.rb
1054
+ - app/models/spree/permission_sets/configuration_management.rb
1055
+ - app/models/spree/permission_sets/dashboard_display.rb
1056
+ - app/models/spree/permission_sets/default_customer.rb
1057
+ - app/models/spree/permission_sets/order_display.rb
1058
+ - app/models/spree/permission_sets/order_management.rb
1059
+ - app/models/spree/permission_sets/product_display.rb
1060
+ - app/models/spree/permission_sets/product_management.rb
1061
+ - app/models/spree/permission_sets/promotion_management.rb
1062
+ - app/models/spree/permission_sets/role_management.rb
1063
+ - app/models/spree/permission_sets/stock_display.rb
1064
+ - app/models/spree/permission_sets/stock_management.rb
1065
+ - app/models/spree/permission_sets/super_user.rb
1066
+ - app/models/spree/permission_sets/user_display.rb
1067
+ - app/models/spree/permission_sets/user_management.rb
1053
1068
  - app/models/spree/policy.rb
1054
1069
  - app/models/spree/post.rb
1055
1070
  - app/models/spree/post_category.rb
@@ -1457,6 +1472,7 @@ files:
1457
1472
  - db/migrate/20250915093930_add_metadata_to_spree_newsletter_subscribers.rb
1458
1473
  - db/migrate/20250923141845_create_spree_imports.rb
1459
1474
  - db/seeds.rb
1475
+ - lib/friendly_id/history_decorator.rb
1460
1476
  - lib/friendly_id/paranoia.rb
1461
1477
  - lib/generators/spree/authentication/custom/custom_generator.rb
1462
1478
  - lib/generators/spree/authentication/custom/templates/authentication_helpers.rb.tt
@@ -1504,6 +1520,7 @@ files:
1504
1520
  - lib/spree/core/importer/product.rb
1505
1521
  - lib/spree/core/number_generator.rb
1506
1522
  - lib/spree/core/partials.rb
1523
+ - lib/spree/core/permission_configuration.rb
1507
1524
  - lib/spree/core/preferences/configuration.rb
1508
1525
  - lib/spree/core/preferences/preferable.rb
1509
1526
  - lib/spree/core/preferences/preferable_class_methods.rb
@@ -1660,9 +1677,9 @@ licenses:
1660
1677
  - BSD-3-Clause
1661
1678
  metadata:
1662
1679
  bug_tracker_uri: https://github.com/spree/spree/issues
1663
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.2.0
1680
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.2.1
1664
1681
  documentation_uri: https://docs.spreecommerce.org/
1665
- source_code_uri: https://github.com/spree/spree/tree/v5.2.0
1682
+ source_code_uri: https://github.com/spree/spree/tree/v5.2.1
1666
1683
  rdoc_options: []
1667
1684
  require_paths:
1668
1685
  - lib