99 lines
3.2 KiB
Ruby
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
|