cartodb/app/models/carto/invitation.rb
2020-06-15 10:58:47 +08:00

99 lines
3.2 KiB
Ruby

require 'active_record'
require 'cartodb-common'
require_dependency 'cartodb/errors'
require_dependency 'carto/user_authenticator'
module Carto
class Invitation < ActiveRecord::Base
# Because of an activerecord-postgresql-array bug that makes array
# insertions unusable we can't set _users_emails mandatory on construction,
# so we create a creator enforcing desired behaviour.
# This will be fixed when we upgrade Ruby and Rails
# validates :users_emails, :welcome_text, presence: true
validates :inviter_user, :organization, :welcome_text, presence: true
validates :users_emails, email: true
validate :users_emails_not_taken
belongs_to :inviter_user, class_name: Carto::User
belongs_to :organization
private_class_method :new
def self.create_new(inviter_user, users_emails, welcome_text, viewer)
raise CartoDB::InvalidUser.new("Only admins can create invitations") unless inviter_user.organization_admin?
# ActiveRecord validation for all values
invitation = new(inviter_user: inviter_user,
organization: inviter_user.organization,
users_emails: users_emails,
welcome_text: welcome_text,
viewer: viewer)
return invitation unless invitation.valid?
# Two-step creation workarounding array bug
invitation = new(inviter_user: inviter_user,
organization: inviter_user.organization,
welcome_text: welcome_text,
viewer: viewer)
invitation.seed = Carto::Common::EncryptionService.make_token
if invitation.save
invitation.reload
invitation.users_emails = users_emails
invitation.used_emails = []
invitation.save
::Resque.enqueue(::Resque::OrganizationJobs::Mail::Invitation, invitation.id)
end
invitation
end
def self.query_with_valid_email(email)
Carto::Invitation.where('? = ANY(users_emails)', email)
end
def self.query_with_unused_email(email)
query_with_valid_email(email).where('? != ALL(used_emails)', email)
end
def token(email)
Carto::Common::EncryptionService.encrypt(sha_class: Digest::SHA256, password: email, salt: seed)
end
def use(email, token)
# reload and used_emails assignment is needed because otherwise
# activerecord-postgresql-array won't update the invitations
reload
if users_emails.include?(email) && verify_token(token, email)
if used_emails.include?(email)
raise AlreadyUsedInvitationError.new("#{email} has already used the invitation")
else
self.used_emails = used_emails.push(email)
save
true
end
else
false
end
end
private
def verify_token(token, email)
Carto::Common::EncryptionService.verify(password: email, secure_password: token, salt: seed)
end
def users_emails_not_taken
return unless users_emails
users_emails.each do |email|
errors[:users_emails] << "Existing user for #{email}" if Carto::User.find_by_email(email)
end
end
end
end
class AlreadyUsedInvitationError < StandardError; end