first attempt at AAL compatibility

will block login of users if their AAL isn't sufficient,
and kill sessions if they later require a higher AAL
This commit is contained in:
Leo McArdle 2019-01-13 19:08:20 +00:00
Родитель c652048f2a
Коммит a0a206b870
11 изменённых файлов: 206 добавлений и 5 удалений

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

@ -31,6 +31,10 @@ module MozillaIAM
log_off_user
else
refresh_iam_session
unless Profile.for(current_user).is_aal_enough?(session[:mozilla_iam].try(:[], :aal))
reset_session
log_off_user
end
end
rescue => e
reset_session

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

@ -18,8 +18,10 @@ module MozillaIAM
::PluginStore.set('mozilla-iam', 'logout_delay', logout_delay)
Rails.cache.write('mozilla-iam/logout_delay', logout_delay)
aal = payload['https://sso.mozilla.com/claim/AAL']
auth_token[:session][:mozilla_iam] = {
last_refresh: Time.now
last_refresh: Time.now,
aal: aal
}
result = Auth::Result.new
@ -35,7 +37,11 @@ module MozillaIAM
result.extra_data = { uid: uid }
if user
Profile.new(user, uid).force_refresh
profile = Profile.new(user, uid)
profile.force_refresh
unless profile.is_aal_enough?(aal)
raise "user logged in with too low an AAL"
end
end
result

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

@ -102,3 +102,4 @@ end
require_relative "profile/update_groups"
require_relative "profile/update_emails"
require_relative "profile/duplicate_accounts"
require_relative "profile/is_aal_enough"

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

@ -0,0 +1,25 @@
module MozillaIAM
Profile.class_eval do
def is_aal_enough?(aal)
aal_levels = [
"UNKNOWN",
"LOW",
"MEDIUM",
"HIGH",
"MAXIMUM"
]
level = aal_levels.index(aal)
level = aal_levels.index("UNKNOWN") if !level
if get(:in_mapped_groups) == "t"
level >= aal_levels.index("MEDIUM")
elsif @user.moderator
level >= aal_levels.index("MEDIUM")
elsif @user.admin
level >= aal_levels.index("MEDIUM")
else
true
end
end
end
end

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

@ -5,13 +5,16 @@ module MozillaIAM
private
def update_groups
in_mapped_groups = false
GroupMapping.all.each do |mapping|
if attr(:groups).include?(mapping.iam_group_name)
in_mapped_groups = true
add_to_group(mapping.group)
else
remove_from_group(mapping.group)
end
end
set(:in_mapped_groups, in_mapped_groups)
end
def add_to_group(group)

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

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

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

@ -125,6 +125,33 @@ describe MozillaIAM::Authenticator do
expect(result.failed).to eq true
end
context "when the AAL" do
let(:user) { Fabricate(:user) }
let(:id_token) { create_id_token(user, { "https://sso.mozilla.com/claim/AAL" => "LOW" }) }
before do
MozillaIAM::Profile.expects(:refresh_methods).returns([:update_groups])
MozillaIAM::GroupMapping.create(iam_group_name: 'iam_group', group: Fabricate(:group))
end
context "is high enough" do
it "authenticates user" do
MozillaIAM::Profile.any_instance.expects(:attr).with(:groups).returns([])
result = authenticate_with_id_token(id_token)
expect(result.user.id).to eq user.id
end
end
context "is too low" do
it "doesn't authenticate user" do
MozillaIAM::Profile.any_instance.expects(:attr).with(:groups).returns(["iam_group"])
result = authenticate_with_id_token(id_token)
expect(result.failed).to eq true
end
end
end
end
context '#after_create_account' do

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

@ -0,0 +1,85 @@
require_relative '../../../iam_helper'
describe MozillaIAM::Profile do
describe "#is_aal_enough?" do
let(:user) { Fabricate(:user) }
let(:profile) { MozillaIAM::Profile.new(user, "uid") }
context "when a user is in no mapped groups" do
before do
profile.expects(:get).with(:in_mapped_groups).returns("f")
end
it "returns true with nil" do
expect(profile.is_aal_enough?(nil)).to eq true
end
it "returns true with UNKNOWN" do
expect(profile.is_aal_enough?("UNKNOWN")).to eq true
end
it "returns true with LOW" do
expect(profile.is_aal_enough?("LOW")).to eq true
end
it "returns true with MEDIUM" do
expect(profile.is_aal_enough?("MEDIUM")).to eq true
end
it "returns true with HIGH" do
expect(profile.is_aal_enough?("HIGH")).to eq true
end
it "returns true with MAXIMUM" do
expect(profile.is_aal_enough?("MAXIMUM")).to eq true
end
end
shared_examples "MEDIUM required" do
it "returns false with nil" do
expect(profile.is_aal_enough?(nil)).to eq false
end
it "returns false with UNKNOWN" do
expect(profile.is_aal_enough?("UNKNOWN")).to eq false
end
it "returns false with LOW" do
expect(profile.is_aal_enough?("LOW")).to eq false
end
it "returns true with MEDIUM" do
expect(profile.is_aal_enough?("MEDIUM")).to eq true
end
it "returns true with HIGH" do
expect(profile.is_aal_enough?("HIGH")).to eq true
end
it "returns true with MAXIMUM" do
expect(profile.is_aal_enough?("MAXIMUM")).to eq true
end
end
context "when a user is in mapped groups" do
before do
profile.expects(:get).with(:in_mapped_groups).returns("t")
end
include_examples "MEDIUM required"
end
context "when a user is a moderator" do
let(:user) { Fabricate(:moderator) }
include_examples "MEDIUM required"
end
context "when a user is an admin" do
let(:user) { Fabricate(:admin) }
include_examples "MEDIUM required"
end
end
end

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

@ -35,6 +35,22 @@ describe MozillaIAM::Profile do
expect(group.users.count).to eq 1
end
context "when a user is in a mapped group" do
it "sets in_mapped_groups to true" do
profile.expects(:attr).with(:groups).returns(['iam_group'])
profile.send(:update_groups)
expect(profile.send(:get, :in_mapped_groups)).to eq "t"
end
end
context "when a user isn't in any mapped groups" do
it "sets in_mapped_groups to false" do
profile.expects(:attr).with(:groups).returns([])
profile.send(:update_groups)
expect(profile.send(:get, :in_mapped_groups)).to eq "f"
end
end
end
context "#add_to_group" do

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

@ -99,6 +99,20 @@ describe TopicsController do
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
@ -134,5 +148,24 @@ describe TopicsController do
end
end
end
context "when the AAL becomes too low" do
it "kills session" do
user = Fabricate(:user)
authenticate_user(user)
log_in_user(user)
session[:mozilla_iam] = { aal: "LOW" }
MozillaIAM::Profile.any_instance.expects(:is_aal_enough?).with("LOW").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("LOW").returns(false)
get :show, params: { id: 666 }, format: :json
expect(session['current_user_id']).to be_nil
end
end
end
end

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

@ -45,7 +45,8 @@ module IAMHelpers
iss: 'https://auth.mozilla.auth0.com/',
aud: 'the_best_client_id',
exp: Time.now.to_i + 7.days,
iat: Time.now.to_i
iat: Time.now.to_i,
"https://sso.mozilla.com/claim/AAL": "UNKNOWN"
}.merge(additional_payload)
header_fields = {
@ -126,7 +127,7 @@ 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)