lib/scrivito/user_definition.rb in scrivito_sdk-0.17.0 vs lib/scrivito/user_definition.rb in scrivito_sdk-0.18.0
- old
+ new
@@ -1,65 +1,69 @@
module Scrivito
- # @api beta
- class UserDefinition < Struct.new(:user_id)
- # Allows user to publish a workspace.
- # @api beta
- PUBLISH_WORKSPACE_ABILITY = 'publish_workspace'
+ # @api public
+ class UserDefinition
+ def initialize(user_id)
+ @user_id = user_id
+ @explicit_rules = {}
+ end
- # Allows user to do everything.
- # @api beta
- ADMINISTRATE_CMS_ABILITY = 'administrate_cms'
+ # Adds an explicit rule, that allows the user to _always_ execute an action.
+ # A rule consists of a verb of the action, the subject of the action and an optional message.
+ # @api public
+ # @param [Symbol] verb the verb of the action (see {Scrivito::User::VERBS}).
+ # @param [Symbol] subject the subject of the action. At the moment only +:workspace+ is supported.
+ # @param [String] message optional message to be displayed in the UI.
+ # @raise [Scrivito::ScrivitoError] if the given verb is invalid
+ # @raise [Scrivito::ScrivitoError] if the specified rule conflicts with a rule specified with
+ # {Scrivito::UserDefinition#can_never}.
+ # @note Normally the memberships of a workspace decide whether a user is allowed or not to
+ # execute a specific action. This method allows to add an exception to this logic and thus
+ # should be used carefully.
+ # @see Scrivito::User::VERBS
+ # @see Scrivito::UserDefinition#can_never
+ # @example User can _always_ read, write and publish a workspace, ignoring the memberships.
+ # Scrivito::User.define('alice') do |user|
+ # user.can_always(:read, :workspace)
+ # user.can_always(:write, :workspace)
+ # user.can_always(:publish, :workspace, 'You can always publish a workspace.)
+ # end
+ def can_always(verb, subject, message = nil)
+ assert_no_conflict(:can_never, verb, subject)
+ @explicit_rules[[:can_always, verb, subject]] = message
+ end
- # @api beta
- ABILITY_NAMES = [
- ADMINISTRATE_CMS_ABILITY,
- PUBLISH_WORKSPACE_ABILITY,
- ]
-
- # Defines a user ability.
- # @api beta
- # @param [String, Symbol] ability_name name of the ability
- # @param [Proc] ability_proc proc to check if the ability can be granted
- # @raise [Scrivito::ScrivitoError] if the given ability name is unknown
- # @raise [Scrivito::ScrivitoError] if no ability proc is given
- # @yieldreturn If the block returns a "truthy" value, then the ability will be granted
- # @yieldreturn If the block returns a "falsy" value, then the ability will be prohibited
- # @see ABILITY_NAMES
- # @see Scrivito::User.define
- # @example
- # allowed_user = nil
- #
- # alice = Scrivito::User.define('alice') do |user|
- # user.can(:publish_workspace) { allowed_user == 'alice' }
+ # Adds an explicit rule, that forbids the user to execute an action.
+ # A rule consists of a verb of the action, the subject of the action and an optional message.
+ # @api public
+ # @param [Symbol] verb the verb of the action (see {Scrivito::User::VERBS}).
+ # @param [Symbol] subject the subject of the action. At the moment only +:workspace+ is supported.
+ # @param [String] message optional message to be displayed in the UI.
+ # @raise [Scrivito::ScrivitoError] if the given verb is invalid
+ # @raise [Scrivito::ScrivitoError] if the specified rule conflicts with a rule specified with
+ # {Scrivito::UserDefinition#can_always}.
+ # @note Normally the memberships of a workspace decide whether a user is allowed or not to
+ # execute a specific action. This method allows to add an exception to this logic and thus
+ # should be used carefully.
+ # @see Scrivito::User::VERBS
+ # @see Scrivito::UserDefinition#can_always
+ # @example User can _never_ publish a workspace, even if she's a workspace owner.
+ # Scrivito::User.define('alice') do |user|
+ # user.can_never(:publish, :workspace, 'You can not publish workspaces.')
# end
- #
- # bob = Scrivito::User.define('bob') do |user|
- # user.can(:publish_workspace) { allowed_user == 'bob' }
- # end
- #
- # # The users `alice` and `bob` will both be not allowed to publish workspace.
- #
- # allowed_user = 'alice'
- # # The user `alice` will now be allowed to publish workspace,
- # # but the user `bob` will still be not allowed.
- #
- # allowed_user = 'bob'
- # # The user `bob` will now be allowed to publish workspace,
- # # but the user `alice` will be not allowed again.
- def can(ability_name, &ability_proc)
- assert_valid_ability(ability_name, ability_proc)
- abilities[ability_name] = ability_proc
+ def can_never(verb, subject, message = nil)
+ assert_no_conflict(:can_always, verb, subject)
+ @explicit_rules[[:can_never, verb, subject]] = message
end
# Defines the user description to be displayed, when the user is shown in the in-place GUI.
- # @api beta
+ # @api public
# @param [Proc] description_proc proc to calculate the description. Defaults to the the user id.
# @note The description is calculated "lazy".
# @note The calculated description will be cached.
# @see Scrivito::User.define
# @example
- # alice = Scrivito::User.define('alice') {}
+ # alice = Scrivito::User.define('alice')
# # User `alice` will be displayed as "alice" in the in-place GUI.
#
# bob = Scrivito::User.define('bob') do |user|
# user.description { 'Bob Doe' }
# end
@@ -68,11 +72,13 @@
@description_proc = description_proc
end
# Defines the proc for fetching users for the user autocompletion of the in-place GUI.
# The user autocompletion is for example used in the details dialog of a workspace.
- # @api beta
+ # If the proc is not set, then {Scrivito::User.find} will be used to fetch the suggested users with input
+ # as the user id.
+ # @api public
# @param [Proc] suggest_users_proc proc for fetching users to be suggested in the in-place GUI
# @yieldparam [String] input an arbitrary string from the input field of a user autocompletion,
# e.g. the first letters of a user name
# @yieldreturn [Array<Scrivito::User>] users that were found for the given input string
# @note Only the first 20 of the returnes users will be displayed in the in-place GUI.
@@ -89,71 +95,68 @@
# end
def suggest_users(&suggest_users_proc)
@suggest_users_proc = suggest_users_proc
end
- # Lets you restrict the ability of a user to publish a certain object. Each
+ # Lets you restrict the rule of a user to publish a certain object. Each
# registered callback can access a certain attribute of an object. Multiple
# callbacks are possible
#
- # @api beta
+ # @api public
# @param [Hash] options
- # @option options [Symbol] :uses the attribute you need in the callback
+ # @option options [Symbol] :using the attribute you need in the callback
# @yield [attribute] the value of the specified attribute
# @yieldreturn [String, false] either return a message for the user or false if
# no restriction is needed
#
# @note the callback is only called with {BasicObj Objs} that have the attribute
- # specified by the :uses option and if it is not a {BasicWidget Widget}-attribute
+ # specified by the :using option and if it is not a {BasicWidget Widget}-attribute
#
# @example
# class MyUserModel
# def to_scrivito_user
# Scrivito::User.define(id) do |user|
- # user.restrict_obj_publish(uses: :_path) do |path|
+ # user.restrict_obj_publish(using: :_path) do |path|
# if path.start_with?("/en")
# false
# else
# "You are only allowed to edit the English site"
# end
# end
#
- # user.restrict_obj_publish(uses: :_obj_class) do |obj_class|
- # if obj_class == "BlogPost"
+ # user.restrict_obj_publish(using: :_obj_class) do |obj_class|
+ # if obj_class.name == 'BlogPost'
# false
# else
- # "You are only allowed to edit Blog Posts"
+ # 'You are only allowed to edit Blog Posts'
# end
# end
# end
# end
# end
def restrict_obj_publish(options, &block)
restriction_set.add(options, &block)
end
def user
- User.new(id: user_id, abilities: abilities, description_proc: @description_proc,
- suggest_users_proc: @suggest_users_proc, restriction_set: restriction_set)
+ User.new(
+ id: @user_id,
+ explicit_rules: @explicit_rules,
+ description_proc: @description_proc,
+ suggest_users_proc: @suggest_users_proc,
+ restriction_set: restriction_set
+ )
end
private
def restriction_set
@restriction_set ||= RestrictionSet.new
end
- def abilities
- @abilities ||= Hash.new(-> { false }).with_indifferent_access
- end
-
- def assert_valid_ability(ability_name, ability_proc)
- unless ABILITY_NAMES.include?(ability_name.to_s)
- raise ScrivitoError.new("'#{ability_name}' is not a valid ability name")
- end
-
- unless ability_proc
- raise ScrivitoError.new("No proc given for ability '#{ability_name}'")
+ def assert_no_conflict(type, verb, subject)
+ if @explicit_rules.has_key?([type, verb, subject])
+ raise ScrivitoError.new("Conflicting rules for verb '#{verb}' and subject '#{subject}'")
end
end
end
end