Merge branch 'development' into cpu-poc

This commit is contained in:
Leo McArdle 2019-04-26 17:11:13 +01:00
Родитель 87882823ae 316f713485
Коммит 01cb63210c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 8262833620A64C3F
18 изменённых файлов: 168 добавлений и 153 удалений

Просмотреть файл

@ -0,0 +1,24 @@
module MozillaIAM
class SessionData < ActiveRecord::Base
TOKEN_COOKIE = Auth::DefaultCurrentUserProvider::TOKEN_COOKIE
def self.find_or_create(session, cookies)
auth_token = cookies[TOKEN_COOKIE]
user_token = UserAuthToken.lookup(auth_token)
if session[:mozilla_iam]
session_data = create!(
user_auth_token_id: user_token.id,
last_refresh: session[:mozilla_iam][:last_refresh],
aal: session[:mozilla_iam][:aal]
)
session.delete(:mozilla_iam)
session_data
else
user_token.mozilla_iam_session_data
end
end
end
end

Просмотреть файл

@ -0,0 +1,5 @@
UserAuthToken.class_eval do
has_one :mozilla_iam_session_data,
class_name: "MozillaIAM::SessionData",
dependent: :destroy
end

Просмотреть файл

@ -26,5 +26,5 @@ mozilla_iam:
shadowed_by_global: true
client: true
mozilla_iam_notification_aud:
default: "hook.prod.sso.mozilla.com"
default: "hook.sso.mozilla.com"
shadowed_by_global: true

Просмотреть файл

@ -0,0 +1,9 @@
class CreateSessionData < ActiveRecord::Migration[5.0]
def change
create_table :mozilla_iam_session_data do |t|
t.integer :user_auth_token_id, null: false
t.datetime :last_refresh
t.string :aal
end
end
end

Просмотреть файл

@ -3,48 +3,37 @@ module MozillaIAM
def check_iam_session
begin
return unless current_user
return if current_user.id < 0
last_refresh = session[:mozilla_iam].try(:[], :last_refresh)
no_refresh = session[:mozilla_iam].try(:[], :no_refresh)
return if no_refresh && !last_refresh
unless last_refresh
current_user.clear_custom_fields
last_refresh = Profile.for(current_user)&.last_refresh
session[:mozilla_iam] = {} if session[:mozilla_iam].nil?
if last_refresh
session[:mozilla_iam][:last_refresh] = last_refresh
else
session[:mozilla_iam][:no_refresh] = true
return
end
end
mozilla_session_data = SessionData.find_or_create(session, request.cookies)
last_refresh = mozilla_session_data.last_refresh
logout_delay =
Rails.cache.fetch('mozilla-iam/logout_delay') do
::PluginStore.get('mozilla-iam', 'logout_delay')
end
if last_refresh + logout_delay < Time.now
reset_session
log_off_user
raise <<~EOF
Mozilla IAM: User session expired
user_id: #{current_user.id}, last_refresh: #{last_refresh}, logout_delay: #{logout_delay}
EOF
else
refresh_iam_session
unless Profile.for(current_user).is_aal_enough?(session[:mozilla_iam].try(:[], :aal))
reset_session
log_off_user
mozilla_session_data.update!(last_refresh: Profile.refresh(current_user))
aal = mozilla_session_data.aal
unless Profile.for(current_user).is_aal_enough?(aal)
raise <<~EOF
Mozilla IAM: AAL not enough, user logged out
user_id: #{current_user.id}, aal: #{aal},
session: #{session.to_hash}
EOF
end
end
rescue => e
Rails.logger.warn("Killed session for user #{current_user.id}: #{e.class} (#{e.message})\n#{e.backtrace.join("\n")}")
reset_session
log_off_user
raise e
end
end
def refresh_iam_session
session[:mozilla_iam][:last_refresh] = Profile.refresh(current_user)
end
end
end

Просмотреть файл

@ -1,6 +1,6 @@
# name: mozilla-iam
# about: A plugin to integrate Discourse with Mozilla's Identity and Access Management (IAM) system
# version: 1.2.0-alpha.15
# version: 1.2.0-alpha.16
# authors: Leo McArdle
# url: https://github.com/mozilla/discourse-mozilla-iam

Просмотреть файл

@ -25,7 +25,7 @@ describe MozillaIAM::Authenticator do
end
it 'can authenticate and create a profile for an existing user' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
result = authenticate_user(user)
uid = user.custom_fields['mozilla_iam_uid']
@ -41,7 +41,7 @@ describe MozillaIAM::Authenticator do
end
it 'can refresh an existing profile for an existing user' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
user.custom_fields['mozilla_iam_uid'] = create_uid(user.username)
user.custom_fields['mozilla_iam_last_refresh'] = Time.now - 14.minutes
user.save_custom_fields
@ -68,7 +68,7 @@ describe MozillaIAM::Authenticator do
end
it 'can refresh an existing profile for an existing user with a new uid' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
old_uid = "the_best_uid"
new_uid = create_uid(user.username)
user.custom_fields['mozilla_iam_uid'] = old_uid
@ -103,7 +103,7 @@ describe MozillaIAM::Authenticator do
end
it 'will not log in with an expired id_token' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
id_token = create_id_token(user, { exp: Time.now, iat: Time.now - 7.days })
result = authenticate_with_id_token(id_token)
@ -112,7 +112,7 @@ describe MozillaIAM::Authenticator do
end
it 'will verify email in sign up form with an id_token with an unverified email' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
id_token = create_id_token(user, { email_verified: false })
result = authenticate_with_id_token(id_token)
@ -120,7 +120,7 @@ describe MozillaIAM::Authenticator do
end
it "won't log in a user if they log in with their secondary email" do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
id_token = create_id_token(user, { email: user.secondary_emails.first })
result = authenticate_with_id_token(id_token)
@ -131,7 +131,7 @@ describe MozillaIAM::Authenticator do
end
context "when the AAL" do
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:id_token) { create_id_token(user, { "https://sso.mozilla.com/claim/AAL" => "LOW" }) }
before do
MozillaIAM::Profile.expects(:refresh_methods).returns([:update_groups])
@ -161,7 +161,7 @@ describe MozillaIAM::Authenticator do
context '#after_create_account' do
it 'can create profile for new user' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
auth = { extra_data: { uid: create_uid(user.username) }}
MozillaIAM::Profile.stubs(:refresh_methods).returns([])

Просмотреть файл

@ -2,7 +2,7 @@ require_relative '../../../iam_helper'
describe MozillaIAM::Profile do
describe "#duplicate_accounts" do
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
context "when no emails are taken" do
@ -12,7 +12,7 @@ describe MozillaIAM::Profile do
end
context "when email is taken" do
let!(:duplicate_user) { Fabricate(:user, email: "taken@email.com") }
let!(:duplicate_user) { Fabricate(:user_with_secondary_email, email: "taken@email.com") }
before { profile.send :set, "taken_emails", ["taken@email.com"] }
it "returns user_id of duplicate account" do
@ -21,8 +21,8 @@ describe MozillaIAM::Profile do
end
context "when emails are taken and one user has multiple" do
let!(:duplicate_user1) { Fabricate(:user, email: "taken1@email.com") }
let!(:duplicate_user2) { Fabricate(:user) }
let!(:duplicate_user1) { Fabricate(:user_with_secondary_email, email: "taken1@email.com") }
let!(:duplicate_user2) { Fabricate(:user_with_secondary_email) }
before do
profile.send :set, "taken_emails", [
"taken1@email.com",
@ -39,7 +39,7 @@ describe MozillaIAM::Profile do
end
context "when email registered as taken isn't" do
let!(:duplicate_user) { Fabricate(:user, email: "taken@email.com") }
let!(:duplicate_user) { Fabricate(:user_with_secondary_email, email: "taken@email.com") }
before do
profile.send :set, "taken_emails", [
"taken@email.com",

Просмотреть файл

@ -2,7 +2,7 @@ require_relative '../../../iam_helper'
describe MozillaIAM::Profile do
describe "#is_aal_enough?" do
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
context "when a user is in no mapped groups" do

Просмотреть файл

@ -7,7 +7,7 @@ describe MozillaIAM::Profile do
let(:email) { "one@email.com" }
let(:secondary_emails) { ["two@email.com", "three@email.com"] }
let(:user) { user = Fabricate(:user_single_email, email: email) }
let(:user) { user = Fabricate(:user, email: email) }
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
def mock_profile_emails(*secondary)
@ -24,7 +24,7 @@ describe MozillaIAM::Profile do
describe "#store_taken_email_or_raise" do
let(:taken_emails) { [] }
before do
u = Fabricate(:user, email: "taken_primary@email.com")
u = Fabricate(:user_with_secondary_email, email: "taken_primary@email.com")
Fabricate(:secondary_email, user: u, email: "taken_secondary@email.com")
end
@ -48,7 +48,7 @@ describe MozillaIAM::Profile do
it "raises exception if it's not because of a taken email" do
begin
Fabricate(:user_email, user: user, email: "second_primary@email.com")
Fabricate(:user_with_secondary_email_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 []
@ -94,7 +94,7 @@ describe MozillaIAM::Profile do
context "and some of those emails are taken" do
before do
u = Fabricate(:user, email: "taken1@email.com")
u = Fabricate(:user_with_secondary_email, 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
@ -127,11 +127,11 @@ describe MozillaIAM::Profile, type: :request do
context "with a non english locale set" do
it "shouldn't fail" do
SiteSetting.allow_user_locale = true
user = Fabricate(:user, locale: :es)
user = Fabricate(:user_with_secondary_email, locale: :es)
authenticate_user(user)
sign_in(user)
Fabricate(:user, email: "taken@email.com")
Fabricate(:user_with_secondary_email, email: "taken@email.com")
described_class.any_instance.stubs(:attr).with(:secondary_emails).returns(["taken@email.com"])
described_class.stubs(:refresh_methods).returns([:update_emails])
user.custom_fields["mozilla_iam_last_refresh"] = Time.now - 1.day

Просмотреть файл

@ -5,7 +5,7 @@ describe MozillaIAM::Profile do
it { should include(:update_groups) }
end
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
context '#update_groups' do

Просмотреть файл

@ -1,7 +1,7 @@
require_relative '../../iam_helper'
describe MozillaIAM::Profile do
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
describe ".during_refresh" do

Просмотреть файл

@ -3,7 +3,7 @@ require_relative '../iam_helper'
describe TopicsController do
context '#check_iam_session' do
it 'does nothing under 15 minutes' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
authenticate_user(user)
log_in_user(user)
@ -14,11 +14,12 @@ describe TopicsController do
get :show, params: { id: 666 }, format: :json
expect(session[:mozilla_iam][:last_refresh]).to be_within(5.seconds).of last_refresh
session_data = MozillaIAM::SessionData.find_or_create({}, request.cookies)
expect(session_data.last_refresh).to be_within(5.seconds).of last_refresh
end
it 'refreshes the session after 15 minutes' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
authenticate_user(user)
log_in_user(user)
@ -28,11 +29,13 @@ describe TopicsController do
session[:mozilla_iam] = { last_refresh: last_refresh }
get :show, params: { id: 666 }, format: :json
expect(session[:mozilla_iam][:last_refresh]).to be_within(5.seconds).of Time.now
session_data = MozillaIAM::SessionData.find_or_create({}, request.cookies)
expect(session_data.last_refresh).to be_within(5.seconds).of Time.now
end
it 'logs off the user after 7 days' do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
authenticate_user(user)
log_in_user(user)
@ -58,103 +61,24 @@ describe TopicsController do
expect(session['current_user_id']).to be_nil
end
context "with no session[:mozilla_iam] set" do
let(:user) { Fabricate(:user) }
before do
context "with no last_refresh" do
it "kills session" do
user = Fabricate(:user_with_secondary_email)
authenticate_user(user)
log_in_user(user)
session[:mozilla_iam] = nil
end
context "and with a user with a last refresh" do
it "fetches last refresh from user profile" do
last_refresh = Time.now - 5.minutes
user.custom_fields['mozilla_iam_last_refresh'] = last_refresh
user.save_custom_fields
get :show, params: { id: 666 }, format: :json
expect(session[:mozilla_iam][:last_refresh]).to be_within(2.seconds).of last_refresh
end
end
context "and with a user with no last refresh" do
it "sets session[:mozilla_iam][:no_refresh] to true" do
user.custom_fields['mozilla_iam_last_refresh'] = nil
user.save_custom_fields
get :show, params: { id: 666 }, format: :json
expect(session[:mozilla_iam][:no_refresh]).to eq true
end
end
context "and with a user with no profile" do
it "sets session[:mozilla_iam][:no_refresh] to true" do
user.custom_fields['mozilla_iam_uid'] = nil
user.save_custom_fields
get :show, params: { id: 666 }, format: :json
expect(session[:mozilla_iam][:no_refresh]).to eq true
end
end
context "and when MEDIUM or above AAL required" do
it "kills session" do
MozillaIAM::Profile.any_instance.expects(:is_aal_enough?).with(nil).returns(true)
get :show, params: { id: 666 }, format: :json
expect(session['current_user_id']).to be
MozillaIAM::Profile.any_instance.expects(:is_aal_enough?).with(nil).returns(false)
get :show, params: { id: 666 }, format: :json
expect(session['current_user_id']).to be_nil
end
end
end
context "with session[:mozilla_iam][:no_refresh] set to true" do
let(:user) { Fabricate(:user) }
before do
authenticate_user(user)
log_in_user(user)
session[:mozilla_iam] = { no_refresh: true }
end
it "doesn't query user profile" do
MozillaIAM::Profile.expects(:for).never
session[:mozilla_iam] = {}
get :show, params: { id: 666 }, format: :json
end
it "doesn't refresh user profile" do
MozillaIAM::Profile.expects(:refresh).never
get :show, params: { id: 666 }, format: :json
end
context "and with session[:mozilla_iam][:last_refresh] set" do
before do
last_refresh = Time.now - 5.minutes
session[:mozilla_iam][:last_refresh] = last_refresh
end
it "refreshes user profile" do
MozillaIAM::Profile.expects(:refresh).once
get :show, params: { id: 666 }, format: :json
end
expect(session['current_user_id']).to be_nil
end
end
context "when the AAL becomes too low" do
it "kills session" do
user = Fabricate(:user)
user = Fabricate(:user_with_secondary_email)
authenticate_user(user)
log_in_user(user)
session[:mozilla_iam] = { aal: "LOW" }
session[:mozilla_iam] = { last_refresh: Time.now, aal: "LOW" }
MozillaIAM::Profile.any_instance.expects(:is_aal_enough?).with("LOW").returns(true)
@ -167,5 +91,20 @@ describe TopicsController do
expect(session['current_user_id']).to be_nil
end
end
context "with system user" do
let(:user) { User.find(-1) }
before do
authenticate_user(user)
log_in_user(user)
end
it "does nothing" do
MozillaIAM::Profile.expects(:for).never
MozillaIAM::Profile.expects(:refresh).never
get :show, params: { id: 666 }, format: :json
end
end
end
end

Просмотреть файл

@ -38,7 +38,7 @@ describe MozillaIAM::NotificationController, type: :request do
let(:jwt) do
create_jwt({
iss: 'https://auth.mozilla.auth0.com/',
aud: 'hook.prod.sso.mozilla.com',
aud: 'hook.sso.mozilla.com',
exp: Time.now.to_i + 7.days,
iat: Time.now.to_i,
}, {

Просмотреть файл

@ -0,0 +1,49 @@
require_relative '../iam_helper'
describe MozillaIAM::SessionData do
it "is destroyed when associated auth token is destroyed" do
end
describe ".find_or_create" do
let!(:last_refresh) { Time.now.round }
let(:aal) { "MEDIUM" }
let(:cookies) { { described_class::TOKEN_COOKIE => "12345" } }
let(:user_auth_token) { UserAuthToken.generate!(user_id: Fabricate(:user_with_secondary_email).id) }
before { UserAuthToken.expects(:lookup).with("12345").returns(user_auth_token) }
context "with session[:mozilla_iam]" do
it "should create SessionData record and remove session[:mozilla_iam]" do
session = {
mozilla_iam: {
last_refresh: last_refresh,
aal: aal
}
}
session_data = described_class.find_or_create(session, cookies)
expect(user_auth_token.mozilla_iam_session_data).to eq session_data
expect(session_data.last_refresh).to eq last_refresh
expect(session_data.aal).to eq aal
expect(session[:mozilla_iam]).to be_nil
end
end
context "without session[:mozilla_iam]" do
it "should find SessionData record" do
described_class.create!(
user_auth_token_id: user_auth_token.id,
last_refresh: last_refresh,
aal: aal
)
session_data = described_class.find_or_create({}, cookies)
expect(user_auth_token.mozilla_iam_session_data).to eq session_data
expect(session_data.last_refresh).to eq last_refresh
expect(session_data.aal).to eq aal
end
end
end
end

Просмотреть файл

@ -8,8 +8,8 @@ describe MozillaIAM do
context 'new post in restricted category' do
let(:poster) { Fabricate(:user) }
let(:user) { Fabricate(:user) }
let(:poster) { Fabricate(:user_with_secondary_email) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:group) { Fabricate(:group, users: [user]) }
let(:category) { Fabricate(:private_category, group: group) }
let(:topic) { Fabricate(:topic, category: category, user: poster) }
@ -83,8 +83,8 @@ describe MozillaIAM do
end
context 'new private message' do
let(:author) { Fabricate(:user) }
let(:user) { Fabricate(:user) }
let(:author) { Fabricate(:user_with_secondary_email) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:post) do
PostCreator.create(author, title: 'private message test',
raw: 'this is my private message',

Просмотреть файл

@ -1,7 +1,7 @@
require_relative '../iam_helper'
describe AdminDetailedUserSerializer do
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:admin) { Fabricate(:admin) }
let(:json) { AdminDetailedUserSerializer.new(user, scope: Guardian.new(admin), root:false).as_json }

Просмотреть файл

@ -17,7 +17,7 @@ describe UserSerializer do
end
shared_context "as another user" do
let(:user2) { Fabricate(:user) }
let(:user2) { Fabricate(:user_with_secondary_email) }
let(:json) { UserSerializer.new(user, scope: Guardian.new(user2), root: false).as_json }
end
@ -26,7 +26,7 @@ describe UserSerializer do
end
context "with secondary emails" do
let(:user) { Fabricate(:user_single_email) }
let(:user) { Fabricate(:user) }
before do
["first", "second"].each do |name|
@ -89,8 +89,8 @@ describe UserSerializer do
end
context "with duplicate accounts (of each other)" do
let(:user) { Fabricate(:user) }
let(:duplicate_user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
let(:duplicate_user) { Fabricate(:user_with_secondary_email) }
before do
profile = MozillaIAM::Profile.new(user, "uid")
@ -149,7 +149,7 @@ describe UserSerializer do
end
describe "#mozilla_iam" do
let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user_with_secondary_email) }
shared_examples "shown" do
include_examples "mozilla_iam in serializer"