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