FEATURE: Add option to have sso synchronize group membership

In some cases add_groups and remove_groups is too much work, some sites
may wish to simply synchronize group membership based on a list.

When sso_overrides_groups is on all not automatic group membership is
sourced from SSO. Note if you omit to specify groups, they will be cleared
out.
This commit is contained in:
Sam 2018-04-10 13:17:23 +10:00
parent ffb7ec2543
commit afaeb20f27
5 changed files with 52 additions and 2 deletions

View File

@ -106,7 +106,29 @@ class DiscourseSingleSignOn < SingleSignOn
private
def synchronize_groups(user)
names = (groups || "").split(",").map(&:downcase)
ids = Group.where('LOWER(NAME) in (?) AND NOT automatic', names).pluck(:id)
group_users = GroupUser
.where('group_id IN (SELECT id FROM groups WHERE NOT automatic)')
.where(user_id: user.id)
group_users.where('group_id NOT IN (?)', ids).destroy_all
ids -= group_users.where('group_id IN (?)', ids).pluck(:group_id)
ids.each do |group_id|
GroupUser.create(group_id: group_id, user_id: user.id)
end
end
def apply_group_rules(user)
if SiteSetting.sso_overrides_groups
synchronize_groups(user)
return
end
if add_groups
split = add_groups.split(",").map(&:downcase)
if split.length > 0

View File

@ -1185,6 +1185,7 @@ en:
sso_url: "URL of single sign on endpoint (must include http:// or https://)"
sso_secret: "Secret string used to cryptographically authenticate SSO information, be sure it is 10 characters or longer"
sso_overrides_bio: "Overrides user bio in user profile and prevents user from changing it"
sso_overrides_groups: "Synchronize all manual group membership with groups specified in the groups sso attribute (WARNING: if you do not specify groups all manual group membership will be cleared for user)"
sso_overrides_email: "Overrides local email with external site email from SSO payload on every login, and prevent local changes. (WARNING: discrepancies can occur due to normalization of local emails)"
sso_overrides_username: "Overrides local username with external site username from SSO payload on every login, and prevent local changes. (WARNING: discrepancies can occur due to differences in username length/requirements)"
sso_overrides_name: "Overrides local full name with external site full name from SSO payload on every login, and prevent local changes."

View File

@ -320,6 +320,7 @@ login:
default: ''
regex: '^https?:\/\/.+[^\/]$'
sso_secret: ''
sso_overrides_groups: false
sso_overrides_bio: false
sso_overrides_email:
default: false

View File

@ -4,7 +4,6 @@ class SingleSignOn
:add_groups, :remove_groups, :groups]
FIXNUMS = []
BOOLS = [:avatar_force_update, :admin, :moderator, :require_activation, :suppress_welcome_message]
ARRAYS = [:groups]
NONCE_EXPIRY_TIME = 10.minutes
attr_accessor(*ACCESSORS)
@ -41,7 +40,6 @@ class SingleSignOn
if BOOLS.include? k
val = ["true", "false"].include?(val) ? val == "true" : nil
end
val = Array(val) if ARRAYS.include?(k) && !val.nil?
sso.send("#{k}=", val)
end

View File

@ -121,6 +121,34 @@ describe DiscourseSingleSignOn do
expect(admin_group.users.where('users.id = ?', user.id).exists?).to eq(true)
end
it "can force a list of groups with the groups attribute" do
user = Fabricate(:user)
group1 = Fabricate(:group, name: 'group1')
group2 = Fabricate(:group, name: 'group2')
sso = DiscourseSingleSignOn.new
sso.username = "bobsky"
sso.name = "Bob"
sso.email = user.email
sso.external_id = "A"
sso.groups = "#{group2.name.capitalize},group4,badname,trust_level_4"
sso.lookup_or_create_user(ip_address)
SiteSetting.sso_overrides_groups = true
group1.reload
expect(group1.usernames).to eq("")
expect(group2.usernames).to eq("")
group1.add(user)
group1.save
sso.lookup_or_create_user(ip_address)
expect(group1.usernames).to eq("")
expect(group2.usernames).to eq(user.username)
end
it "can specify groups" do
user = Fabricate(:user)