Class: Decidim::AuthorizationTransfer

Inherits:
ApplicationRecord show all
Defined in:
app/models/decidim/authorization_transfer.rb

Overview

An authorization transfer object is created when the user authorizes themselves with the same unique ID as some other user had used in the past. Typically this can happen if the user first created an account and authorized it, then deleted their account, and finally decided to register again and authorize their account for a second time.

To register an authorization transfer handler in a specific module, use the following code example.

The handler registration needs a name for the specific module handling the transfer and a block which handles the specific transfer. The block is called with the transfer record, i.e. instance of this class with access to all necessary information required for handling the transfer.

Examples:

Register authorization handler

Decidim::AuthorizationTransfer.register(:my_module) do |transfer, auth_handler|
  # The move_records method updates the provided active record objects
  # to be mapped to the new user for which the authorization is being
  # transferred to. Provide the record class and the column name which
  # maps the user records to these records as its arguments.
  #
  # If you need access to the authorization handler that caused the
  # transfer to be initiated, it is available as the second yielded
  # argument (auth_hander).
  transfer.move_records(Decidim::MyModule::FooBar, :decidim_user_id)
end

Defined Under Namespace

Classes: DisabledError

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.disable!Boolean

Disables the authorization transfer functionality.

Returns:

  • (Boolean)

    Returns the enabled status after the disabling, i.e. false.



115
116
117
# File 'app/models/decidim/authorization_transfer.rb', line 115

def disable!
  @enabled = false
end

.enable!Boolean

Enables the authorization transfer functionality. By default the functionality is already enabled, so this method is only needed in case the enabled state is changed e.g. during tests.

Returns:

  • (Boolean)

    Returns the enabled status after the enabling, i.e. true.



107
108
109
# File 'app/models/decidim/authorization_transfer.rb', line 107

def enable!
  @enabled = true
end

.enabled?Boolean

Returns the enabled status for the authorization transfers. True by default.

Returns:

  • (Boolean)

    True if the authorization transfers are enabled and false if they are disabled.



95
96
97
98
99
# File 'app/models/decidim/authorization_transfer.rb', line 95

def enabled?
  enable! if @enabled.nil?

  @enabled
end

.perform!(authorization, handler) ⇒ Decidim::AuthorizationTransfer

Performs the authorization transfer for the provided authorization object with the provided handler which is authorizing the user.

Parameters:

  • authorization (Decidim::Authorization)

    The authorization object to be transferred over to the new user indicated by the authorization handler.

  • handler (Decidim::AuthorizationHandler)

    The authorization handler object with all the necessary information for authorizing the new user. The target user for which the authorization is transferred over to is fetched from the handler.

Returns:

Raises:



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'app/models/decidim/authorization_transfer.rb', line 132

def perform!(authorization, handler)
  raise DisabledError unless enabled?

  transaction do
    transfer = create!(
      authorization:,
      user: handler.user,
      source_user: authorization.user
    )

    transfer.announce!(handler)

    # Update the metadata, transfer to the new user and grant.
    authorization.attributes = {
      metadata: handler.,
      user: handler.user
    }

    authorization.grant!

    transfer
  end
end

.register(name) {|transfer, authorization_handler| ... } ⇒ Proc

Expose the methods provided by the registry singleton through the model class.

Registers an authorization transfer handler for a specific use case in any modules that need to handle authorization transfers.

Examples:

Decidim::AuthorizationTransfer.register(:my_module) do |transfer, auth_handler|
  transfer.move_records(Decidim::MyModule::FooBar, :decidim_user_id)
end

Parameters:

  • name (Symbol)

    The name for the block, e.g. β€˜:proposals`.

Yields:

  • (transfer, authorization_handler)

    Handles the authorization transfer for the given context. This is called before the authorization is transferred over to the new user and granted.

Yield Parameters:

  • The (Decidim::AuthorizationTransfer)

    authorization transfer being processed which stores all the necessary information about the transfer, related authorization and related user and source user (i.e. the user from which the authorization is transferred from).

  • The (Decidim::AuthorizationHandler)

    authorization handler in charge of the current authorization action which initiated the transfer due to the duplicate unique ID it detected. This handler can provide access to the metadata from the new authorization action that caused the conflict to happen.

Returns:

  • (Proc)

    The registered block itself.

See Also:



88
# File 'app/models/decidim/authorization_transfer.rb', line 88

delegate :register, :unregister, :registrations, to: :registry

.registrations {|transfer, authorization_handler| ... } ⇒ Proc

Expose the methods provided by the registry singleton through the model class.

Registers an authorization transfer handler for a specific use case in any modules that need to handle authorization transfers.

Examples:

Decidim::AuthorizationTransfer.register(:my_module) do |transfer, auth_handler|
  transfer.move_records(Decidim::MyModule::FooBar, :decidim_user_id)
end

Parameters:

  • name (Symbol)

    The name for the block, e.g. β€˜:proposals`.

Yields:

  • (transfer, authorization_handler)

    Handles the authorization transfer for the given context. This is called before the authorization is transferred over to the new user and granted.

Yield Parameters:

  • The (Decidim::AuthorizationTransfer)

    authorization transfer being processed which stores all the necessary information about the transfer, related authorization and related user and source user (i.e. the user from which the authorization is transferred from).

  • The (Decidim::AuthorizationHandler)

    authorization handler in charge of the current authorization action which initiated the transfer due to the duplicate unique ID it detected. This handler can provide access to the metadata from the new authorization action that caused the conflict to happen.

Returns:

  • (Proc)

    The registered block itself.

See Also:



88
# File 'app/models/decidim/authorization_transfer.rb', line 88

delegate :register, :unregister, :registrations, to: :registry

.registryDecidim::BlockRegistry

Provides access to the registry instance that stores the transfer handlers for each module.

The only reason for the registry is defined at the Decidim core module is to have it in the β€˜lib` folder which is not reloaded on every request at the development environment. If the registry was stored within the model class itself, it would be empty after every code reload (i.e. every request).

Returns:



51
52
53
# File 'app/models/decidim/authorization_transfer.rb', line 51

def registry
  Decidim.authorization_transfer_registry
end

.unregister(*names) {|transfer, authorization_handler| ... } ⇒ Proc

Expose the methods provided by the registry singleton through the model class.

Registers an authorization transfer handler for a specific use case in any modules that need to handle authorization transfers.

Examples:

Decidim::AuthorizationTransfer.register(:my_module) do |transfer, auth_handler|
  transfer.move_records(Decidim::MyModule::FooBar, :decidim_user_id)
end

Parameters:

  • name (Symbol)

    The name for the block, e.g. β€˜:proposals`.

Yields:

  • (transfer, authorization_handler)

    Handles the authorization transfer for the given context. This is called before the authorization is transferred over to the new user and granted.

Yield Parameters:

  • The (Decidim::AuthorizationTransfer)

    authorization transfer being processed which stores all the necessary information about the transfer, related authorization and related user and source user (i.e. the user from which the authorization is transferred from).

  • The (Decidim::AuthorizationHandler)

    authorization handler in charge of the current authorization action which initiated the transfer due to the duplicate unique ID it detected. This handler can provide access to the metadata from the new authorization action that caused the conflict to happen.

Returns:

  • (Proc)

    The registered block itself.

See Also:



88
# File 'app/models/decidim/authorization_transfer.rb', line 88

delegate :register, :unregister, :registrations, to: :registry

Instance Method Details

#announce!(handler) ⇒ Array<Proc>

This announces the transfer to external modules that can perform their own actions during the authorization transfer. This is called before the authorization is transferred to the new user allowing different modules to transfer their records from the source user to the user that the authorization is being transferred to. Note that during the publish event, the authorization record is still pointing to the source user but the transfer record itself available for the event is mapped correctly to the target user.

Parameters:

  • handler (Decidim::AuthorizationHandler)

    The authorization handler for the transfer procedure which contains all the necessary information about the data that was submitted from the authorization action. It is yielded to the registered transfer handlers as the second argument of the registered block.

Returns:

  • (Array<Proc>)

    An array of the blocks that were processed during the transfer.

Raises:



181
182
183
184
185
186
187
# File 'app/models/decidim/authorization_transfer.rb', line 181

def announce!(handler)
  raise DisabledError unless self.class.enabled?

  self.class.registrations.values.each do |block|
    block.call(self, handler)
  end
end

#informationHash<String => Hash<Symbol => Integer, ActiveModel::Name>>

Returns information about the transfer in the described format. The returned hash contains information about the transferred records as the record type (class name as string) as its keys and an informational hash as its values with the following keys:

  • :class - The class constant of the transferred records

  • :count - Number of the records of this type that were transferred

  • :name - An instance of ActiveModel::Name for the record class

Examples:

Format of the returned information hash

{
  "Decidim::Foo" => {
     class: Decidim::Foo,
     count: 123,
     name: ActiveModel::Name.new(Decidim::Foo)
  },
  "Decidim::Bar" => {
     class: Decidim::Bar,
     count: 456,
     name: ActiveModel::Name.new(Decidim::Bar)
  }
}

Returns:

  • (Hash<String => Hash<Symbol => Integer, ActiveModel::Name>>)

    The information hash created for the transfer.



220
221
222
223
224
225
226
227
228
229
230
# File 'app/models/decidim/authorization_transfer.rb', line 220

def information
  {}.tap do |types|
    records.find_each do |record|
      resource_class = record.type.safe_constantize
      next unless resource_class

      types[record.type] ||= { class: resource_class, count: 0 }
      types[record.type][:count] += 1
    end
  end
end

#move_records(resource_class, user_column) ⇒ Array<Decidim::AuthorizationTransferRecord>

Handles moving records from the source user to the user to which the authorization is being transferred to. This updates the provided user column of the provided class to the user being authorized.

Parameters:

  • resource_class (Class)

    The resource class for which records should be transferred for.

  • user_column (Symbol, String)

    The User column to be updated for the records. It is updated with the user mapped to the transfer, i.e. the target user.

Returns:



243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'app/models/decidim/authorization_transfer.rb', line 243

def move_records(resource_class, user_column)
  transferrable_records = resource_class.where(user_column => source_user_id)
  transferrable_ids = transferrable_records.pluck(:id)

  # rubocop:disable Rails::SkipsModelValidations
  transferrable_records.update_all(user_column => user_id)
  # rubocop:enable Rails::SkipsModelValidations

  records.create!(
    transferrable_ids.map do |resource_id|
      { resource_type: resource_class.name, resource_id: }
    end
  )
end

#presenterDecidim::AuthorizationTransferPresenter

Creates a presenter instance for this record and returns it.

Returns:



192
193
194
# File 'app/models/decidim/authorization_transfer.rb', line 192

def presenter
  AuthorizationTransferPresenter.new(self)
end

#readonly?Boolean

Overwrites the method so that records cannot be modified.

Returns:

  • (Boolean)

    A boolean indicating whether the record is read only.



160
161
162
# File 'app/models/decidim/authorization_transfer.rb', line 160

def readonly?
  !new_record? && !destroyed_by_association
end