set secondary email addresses and store taken ones
This commit is contained in:
Родитель
ecc1b18a23
Коммит
46fa806fa1
|
@ -23,4 +23,24 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class='display-row secondary-emails'>
|
||||
<div class='field'>Taken Emails</div>
|
||||
|
||||
<div class='value'>
|
||||
{{#if model.email}}
|
||||
{{#if model.mozilla_iam.taken_emails}}
|
||||
<ul>
|
||||
{{#each model.mozilla_iam.taken_emails as |email| }}
|
||||
<li><a href="mailto:{{unbound email}}">{{email}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
{{else}}
|
||||
Click " {{d-icon "envelope-o"}} Show " above
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
|
|
@ -5,7 +5,9 @@ module MozillaIAM
|
|||
object.custom_fields.select do |k, v|
|
||||
k.start_with?('mozilla_iam')
|
||||
end.map do |k, v|
|
||||
[k.sub('mozilla_iam_', ''), v]
|
||||
key = k.sub('mozilla_iam_', '')
|
||||
val = Array(v) if Profile.array_keys.include?(key.to_sym)
|
||||
[key, val || v]
|
||||
end.to_h
|
||||
end
|
||||
|
||||
|
|
|
@ -16,10 +16,12 @@ module MozillaIAM
|
|||
|
||||
class Profile
|
||||
attr_reader :groups
|
||||
attr_reader :secondary_emails
|
||||
|
||||
def initialize(raw)
|
||||
@raw = raw
|
||||
@groups = Array(raw[:groups]) | Array(raw.dig(:app_metadata, :groups))
|
||||
@secondary_emails = Array(raw[:email_aliases])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,10 +11,29 @@ module MozillaIAM
|
|||
end
|
||||
|
||||
def profile(uid)
|
||||
profile = get("profile/#{uid}")
|
||||
MultiJson.load(profile[:body], symbolize_keys: true)
|
||||
res = get("profile/#{uid}")
|
||||
Profile.new(MultiJson.load(res[:body], symbolize_keys: true))
|
||||
end
|
||||
|
||||
class Profile
|
||||
attr_reader :secondary_emails
|
||||
|
||||
def initialize(raw)
|
||||
@raw = raw
|
||||
@secondary_emails = process_emails
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_emails
|
||||
emails = @raw[:emails]
|
||||
if emails
|
||||
emails.select { |x| x[:verified] && !x[:primary] }.map { |x| x[:value] }.uniq
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
module MozillaIAM
|
||||
class Profile
|
||||
@refresh_methods = []
|
||||
@array_keys = []
|
||||
|
||||
class << self
|
||||
attr_accessor :refresh_methods
|
||||
attr_accessor :array_keys
|
||||
|
||||
def during_refresh(method_name)
|
||||
refresh_methods << method_name
|
||||
end
|
||||
|
||||
def register_as_array(key)
|
||||
array_keys << key
|
||||
end
|
||||
|
||||
def refresh(user)
|
||||
uid = get(user, :uid)
|
||||
return if uid.blank?
|
||||
|
@ -43,6 +49,8 @@ module MozillaIAM
|
|||
value = @api_profiles[api.name].send(attr)
|
||||
if response.nil?
|
||||
response = value
|
||||
elsif [response, value].map { |x| x.kind_of? Array }.all?
|
||||
response = response | value
|
||||
end
|
||||
end
|
||||
return response
|
||||
|
@ -87,3 +95,4 @@ module MozillaIAM
|
|||
end
|
||||
|
||||
require_relative "profile/update_groups"
|
||||
require_relative "profile/update_emails"
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
module MozillaIAM
|
||||
Profile.class_eval do
|
||||
during_refresh :update_emails
|
||||
register_as_array :taken_emails
|
||||
|
||||
private
|
||||
|
||||
def store_taken_email_or_raise(e, taken_emails)
|
||||
raise e unless e.message == "Validation failed: Email has already been taken"
|
||||
taken_emails << e.record.email
|
||||
end
|
||||
|
||||
def update_emails
|
||||
emails = attr(:secondary_emails)
|
||||
taken_emails = []
|
||||
@user.user_emails.where(primary: false).where.not(email: emails).delete_all
|
||||
emails.each do |email|
|
||||
begin
|
||||
UserEmail.find_or_create_by!(user: @user, email: email)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
store_taken_email_or_raise(e, taken_emails)
|
||||
end
|
||||
end
|
||||
set(:taken_emails, taken_emails)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -69,5 +69,22 @@ describe MozillaIAM::API::Management do
|
|||
include_examples "empty array"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#secondary_emails" do
|
||||
it "returns content of email_aliases" do
|
||||
profile = described_class.new({ email_aliases: ["one", "two"] })
|
||||
expect(profile.secondary_emails).to contain_exactly "one", "two"
|
||||
end
|
||||
|
||||
it "is empty array when email_alises is empty" do
|
||||
profile = described_class.new({ email_aliases: [] })
|
||||
expect(profile.secondary_emails).to eq []
|
||||
end
|
||||
|
||||
it "is empty array email_alises is nil" do
|
||||
profile = described_class.new({ })
|
||||
expect(profile.secondary_emails).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,12 +17,79 @@ describe MozillaIAM::API::Person do
|
|||
context "#profile" do
|
||||
it "returns the profile for a specific user" do
|
||||
api.expects(:get).with("profile/uid").returns(body: '{"profile":"profile"}')
|
||||
expect(api.profile("uid")[:profile]).to eq "profile"
|
||||
expect(api.profile("uid").instance_variable_get(:@raw)).to eq({profile: "profile"})
|
||||
end
|
||||
|
||||
it "returns an empty hash if a profile doesn't exist" do
|
||||
api.expects(:get).with("profile/uid").returns(body: '{}')
|
||||
expect(api.profile("uid")).to eq({})
|
||||
expect(api.profile("uid").instance_variable_get(:@raw)).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
describe described_class::Profile do
|
||||
describe "#secondary_emails" do
|
||||
context "with no emails attribute in profile" do
|
||||
let(:profile) { described_class.new({}) }
|
||||
|
||||
it "returns empty array" do
|
||||
expect(profile.secondary_emails).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context "with empy emails attribute in profile" do
|
||||
let(:profile) { described_class.new({ emails: [] }) }
|
||||
|
||||
it "returns empty array" do
|
||||
expect(profile.secondary_emails).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context "with primary email in profile" do
|
||||
let(:profile) { described_class.new({ emails: [
|
||||
{ verified: true, value: "first@example.com", primary: true }
|
||||
] }) }
|
||||
|
||||
it "returns empty array" do
|
||||
expect(profile.secondary_emails).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context "with multiple primary emails in profile" do
|
||||
let(:profile) { described_class.new({ emails: [
|
||||
{ verified: true, value: "first@example.com", primary: true },
|
||||
{ verified: true, value: "second@example.com", primary: true }
|
||||
] }) }
|
||||
|
||||
it "returns empty array" do
|
||||
expect(profile.secondary_emails).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context "secondary emails in profile" do
|
||||
let(:profile) { described_class.new({ emails: [
|
||||
{ verified: true, value: "first@example.com", primary: true },
|
||||
{ verified: true, value: "second@example.com", primary: false },
|
||||
{ verified: true, value: "third@example.com", primary: false },
|
||||
] }) }
|
||||
|
||||
it "returns secondary emails" do
|
||||
expect(profile.secondary_emails).to contain_exactly("second@example.com", "third@example.com")
|
||||
end
|
||||
end
|
||||
|
||||
context "with unverified emails in profile" do
|
||||
let(:profile) { described_class.new({ emails: [
|
||||
{ verified: false, value: "first_unverified@example.com", primary: true },
|
||||
{ verified: true, value: "first@example.com", primary: true },
|
||||
{ verified: false, value: "second_unverified@example.com", primary: true },
|
||||
{ verified: true, value: "second@example.com", primary: false },
|
||||
{ verified: true, value: "third@example.com", primary: false },
|
||||
] }) }
|
||||
|
||||
it "returns verified secondary emails" do
|
||||
expect(profile.secondary_emails).to contain_exactly("second@example.com", "third@example.com")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
require_relative '../../../iam_helper'
|
||||
|
||||
describe MozillaIAM::Profile do
|
||||
describe described_class.refresh_methods do
|
||||
it { should include(:update_emails) }
|
||||
end
|
||||
|
||||
let(:email) { "one@email.com" }
|
||||
let(:secondary_emails) { ["two@email.com", "three@email.com"] }
|
||||
let(:user) { user = Fabricate(:user_single_email, email: email) }
|
||||
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
|
||||
|
||||
def mock_profile_emails(*secondary)
|
||||
profile.stubs(:attr).with(:secondary_emails).returns(secondary)
|
||||
end
|
||||
|
||||
before do
|
||||
secondary_emails.each do |email|
|
||||
Fabricate(:secondary_email, user: user, email: email)
|
||||
end
|
||||
mock_profile_emails(*secondary_emails)
|
||||
end
|
||||
|
||||
describe "#store_taken_email_or_raise" do
|
||||
let(:taken_emails) { [] }
|
||||
before do
|
||||
u = Fabricate(:user, email: "taken_primary@email.com")
|
||||
Fabricate(:secondary_email, user: u, email: "taken_secondary@email.com")
|
||||
end
|
||||
|
||||
it "stores email if secondary email is taken as a primary email" do
|
||||
begin
|
||||
Fabricate(:secondary_email, user: user, email: "taken_primary@email.com")
|
||||
rescue Exception => e
|
||||
profile.send(:store_taken_email_or_raise, e, taken_emails)
|
||||
expect(taken_emails).to contain_exactly("taken_primary@email.com")
|
||||
end
|
||||
end
|
||||
|
||||
it "stores email if secondary email is taken as secondary email" do
|
||||
begin
|
||||
Fabricate(:secondary_email, user: user, email: "taken_secondary@email.com")
|
||||
rescue Exception => e
|
||||
profile.send(:store_taken_email_or_raise, e, taken_emails)
|
||||
expect(taken_emails).to contain_exactly("taken_secondary@email.com")
|
||||
end
|
||||
end
|
||||
|
||||
it "raises exception if it's not because of a taken email" do
|
||||
begin
|
||||
Fabricate(:user_email, user: user, email: "second_primary@email.com")
|
||||
rescue Exception => e
|
||||
expect { profile.send(:store_taken_email_or_raise, e, taken_emails) }.to raise_exception e
|
||||
expect(taken_emails).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update_emails" do
|
||||
shared_examples "leaves primary" do
|
||||
it "leaves primary email alone" do
|
||||
profile.send(:update_emails)
|
||||
expect(user.email).to eq email
|
||||
end
|
||||
end
|
||||
|
||||
include_examples "leaves primary"
|
||||
|
||||
it "leaves secondary emails alone" do
|
||||
profile.send(:update_emails)
|
||||
expect(user.secondary_emails).to match_array secondary_emails
|
||||
end
|
||||
|
||||
context "when profile has no secondary emails" do
|
||||
before { mock_profile_emails() }
|
||||
|
||||
include_examples "leaves primary"
|
||||
|
||||
it "removes seconary emails" do
|
||||
profile.send(:update_emails)
|
||||
expect(user.secondary_emails).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context "when profile has different secondary emails" do
|
||||
before { mock_profile_emails("two@email.com", "four@email.com") }
|
||||
|
||||
include_examples "leaves primary"
|
||||
|
||||
it "updates secondary emails" do
|
||||
profile.send(:update_emails)
|
||||
expect(user.secondary_emails).to contain_exactly("two@email.com", "four@email.com")
|
||||
end
|
||||
|
||||
context "and some of those emails are taken" do
|
||||
before do
|
||||
u = Fabricate(:user, email: "taken1@email.com")
|
||||
Fabricate(:secondary_email, user: u, email: "taken2@email.com")
|
||||
mock_profile_emails("taken1@email.com", "two@email.com", "taken2@email.com", "four@email.com")
|
||||
end
|
||||
|
||||
include_examples "leaves primary"
|
||||
|
||||
it "stores taken emails, and updates the rest" do
|
||||
profile.send(:update_emails)
|
||||
expect(profile.send(:get, :taken_emails)).to contain_exactly("taken1@email.com", "taken2@email.com")
|
||||
expect(user.secondary_emails).to contain_exactly("two@email.com", "four@email.com")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,6 +19,21 @@ describe MozillaIAM::Profile do
|
|||
end
|
||||
end
|
||||
|
||||
describe ".register_as_array" do
|
||||
it "adds key to .array_keys" do
|
||||
described_class.register_as_array :foo
|
||||
expect(described_class.array_keys).to include :foo
|
||||
|
||||
described_class.register_as_array :bar
|
||||
expect(described_class.array_keys).to include :bar
|
||||
|
||||
described_class.array_keys.delete(:foo)
|
||||
described_class.array_keys.delete(:bar)
|
||||
expect(described_class.array_keys).not_to include :foo
|
||||
expect(described_class.array_keys).not_to include :bar
|
||||
end
|
||||
end
|
||||
|
||||
context '.refresh' do
|
||||
it "refreshes a user who already has a profile" do
|
||||
profile
|
||||
|
@ -101,6 +116,7 @@ describe MozillaIAM::Profile do
|
|||
def initialize(rand); @rand = rand; end
|
||||
def foo; :foo; end
|
||||
def foobar; :foo; end
|
||||
def array; [1, 2, 3]; end
|
||||
end
|
||||
end
|
||||
class Bar
|
||||
|
@ -108,6 +124,7 @@ describe MozillaIAM::Profile do
|
|||
class Profile
|
||||
def bar; :bar; end
|
||||
def foobar; :bar; end
|
||||
def array; [3, 4, 5]; end
|
||||
end
|
||||
end
|
||||
MozillaIAM::API.stubs(:profile_apis).returns([Foo, Bar])
|
||||
|
@ -132,6 +149,10 @@ describe MozillaIAM::Profile do
|
|||
MozillaIAM::API.stubs(:profile_apis).returns([Bar, Foo])
|
||||
expect(profile.attr(:foobar)).to eq :bar
|
||||
end
|
||||
|
||||
it "takes the union of the attribute if it's an array" do
|
||||
expect(profile.attr(:array)).to contain_exactly(1, 2, 3, 4, 5)
|
||||
end
|
||||
end
|
||||
|
||||
it "caches Profile instances" do
|
||||
|
|
|
@ -28,7 +28,7 @@ describe MozillaIAM do
|
|||
end
|
||||
|
||||
context 'when user in correct IAM group' do
|
||||
before { stub_management_api_profile_request(uid, groups: ['iam_group']) }
|
||||
before { stub_apis_profile_request(uid, groups: ['iam_group']) }
|
||||
|
||||
it 'refreshes the user profile' do
|
||||
PostAlerter.post_created(reply)
|
||||
|
@ -55,7 +55,7 @@ describe MozillaIAM do
|
|||
end
|
||||
|
||||
context 'when user removed from IAM group' do
|
||||
before { stub_management_api_profile_request(uid, groups: []) }
|
||||
before { stub_apis_profile_request(uid, groups: []) }
|
||||
|
||||
it 'refreshes the user profile' do
|
||||
PostAlerter.post_created(reply)
|
||||
|
|
|
@ -25,5 +25,14 @@ describe AdminDetailedUserSerializer do
|
|||
|
||||
expect(json[:mozilla_iam]).to be_empty
|
||||
end
|
||||
|
||||
it "should return registered custom fields as arrays" do
|
||||
MozillaIAM::Profile.stubs(:array_keys).returns([:array])
|
||||
|
||||
user.custom_fields['mozilla_iam_array'] = "element"
|
||||
|
||||
mozilla_iam = json[:mozilla_iam]
|
||||
expect(mozilla_iam['array']).to eq ["element"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,6 +72,7 @@ module IAMHelpers
|
|||
end
|
||||
|
||||
def authenticate_user(user)
|
||||
MozillaIAM::Profile.stubs(:refresh_methods).returns([])
|
||||
authenticate_with_id_token create_id_token(user)
|
||||
end
|
||||
|
||||
|
@ -90,17 +91,25 @@ module IAMHelpers
|
|||
kid: 'the_best_key'
|
||||
}
|
||||
|
||||
req_body = {
|
||||
audience: aud,
|
||||
client_id: "the_best_client_id",
|
||||
client_secret: "",
|
||||
grant_type: "client_credentials"
|
||||
}
|
||||
|
||||
access_token = JWT.encode(payload, private_key, 'RS256', header_fields)
|
||||
body = MultiJson.dump(access_token: access_token)
|
||||
res_body = MultiJson.dump(access_token: access_token)
|
||||
|
||||
stub_request(:post, 'https://auth.mozilla.auth0.com/oauth/token')
|
||||
.to_return(status: 200, body: body)
|
||||
.with(body: req_body)
|
||||
.to_return(status: 200, body: res_body)
|
||||
end
|
||||
|
||||
def stub_people_api_profile_request(uid, profile)
|
||||
stub_oauth_token_request('https://person-api.sso.mozilla.com')
|
||||
|
||||
stub_request(:get, "https://uhbz4h3wa8.execute-api.us-west-2.amazonaws.com/prod/profile/#{uid}")
|
||||
stub_request(:get, "https://person-api.sso.mozilla.com/v1/profile/#{uid}")
|
||||
.to_return(status: 200, body: MultiJson.dump(body: MultiJson.dump(profile)))
|
||||
end
|
||||
|
||||
|
@ -117,4 +126,9 @@ module IAMHelpers
|
|||
expect { parent.const_get(const) }.to raise_error(NameError)
|
||||
end
|
||||
end
|
||||
|
||||
def stub_apis_profile_request(uid, profile)
|
||||
stub_management_api_profile_request(uid, profile)
|
||||
stub_people_api_profile_request(uid, profile)
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче