Skip to content

Commit 2b5d297

Browse files
committed
Add support for synchronizing groups
1 parent bcdcdf5 commit 2b5d297

File tree

7 files changed

+70
-0
lines changed

7 files changed

+70
-0
lines changed

README.rdoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ admin_role::
8787
+realm_access.roles+.
8888

8989
Example: ROLES/REDMINE/ADMIN
90+
groups_claim::
91+
The claim which provides the set of groups (optional).
92+
93+
Example: +groups+
94+
group_names_pattern::
95+
A regex pattern to filter names of user groups that should be synchronized.
96+
Default is +.*+ (match all).
97+
98+
Example: +^(Red|Blue)$+
9099

91100

92101
== Mapping users

app/controllers/oidc_controller.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,42 @@ def create_user
8686
user.activate
8787
user.random_password
8888
user.last_login_on = Time.now
89+
update_groups(user)
8990
user.save ? successful_login(user) : unsuccessful_login(user)
9091
end
9192

9293
def update_user(user)
9394
user.update(@oidc_session.user_attributes)
9495
user.activate
9596
user.update_last_login_on!
97+
update_groups(user)
9698
user.save ? successful_login(user) : unsuccessful_login(user)
9799
end
98100

101+
def update_groups(user)
102+
return unless settings.update_groups?
103+
104+
current = user.groups.select { |group| settings.group_names_regexp.match?(group.name) }
105+
106+
target = @oidc_session.groups.map do |name|
107+
begin
108+
Group.named(name).first_or_create!(name: name)
109+
rescue ActiveRecord::RecordInvalid => e
110+
logger.error "Failed to create group #{name}: #{e}"
111+
end
112+
end
113+
114+
unless (added = target - current).empty?
115+
logger.info "Adding user #{user.login} to groups: #{added.map(:name).join(', ')}"
116+
user.groups += added
117+
end
118+
119+
unless (removed = current - target).empty?
120+
logger.info "Removing user #{user.login} from groups: #{removed.map(:name).join(', ')}"
121+
user.groups -= removed
122+
end
123+
end
124+
99125
def successful_login(user)
100126
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
101127
oidc_session = OidcSession.spawn(session)
@@ -110,4 +136,7 @@ def unsuccessful_login(user)
110136
end
111137
end
112138

139+
def settings
140+
@settings ||= RedmineOidc.settings
141+
end
113142
end

app/models/oidc_session.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ def user_attributes
103103
}
104104
end
105105

106+
def groups
107+
@groups ||= if settings.update_groups?
108+
(decoded_id_token.raw_attributes[settings.groups_claim] || [])
109+
.select { |name| settings.group_names_regexp.match?(name) }
110+
.to_set
111+
else
112+
Set.new
113+
end
114+
end
115+
106116
def refresh_token_expiration_timestamp
107117
decoded_refresh_token['exp']
108118
end

app/views/settings/_redmine_oidc.html.erb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@
3838
<%= label_tag 'settings[admin_role]', l('oidc.settings.admin_role') %>
3939
<%= text_field_tag 'settings[admin_role]', oidc_settings.admin_role, size: 60 %>
4040
</p>
41+
<p>
42+
<%= label_tag 'settings[groups_claim]', l('oidc.settings.groups_claim') %>
43+
<%= text_field_tag 'settings[groups_claim]', oidc_settings.groups_claim, size: 60 %>
44+
</p>
45+
<p>
46+
<%= label_tag 'settings[group_names_pattern]', l('oidc.settings.group_names_pattern') %>
47+
<%= text_field_tag 'settings[group_names_pattern]', oidc_settings.group_names_pattern, size: 60 %>
48+
</p>
4149
<p>
4250
<%= label_tag 'settings[session_check_enabled]', l('oidc.settings.session_check_enabled') %>
4351
<%= check_box_tag 'settings[session_check_enabled]', 1, oidc_settings.session_check_enabled %>

config/locales/de.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ de:
3434
roles_claim_placeholder: roles
3535
access_roles: Leerzeichen-separierte Liste der autorisierten Rollen
3636
admin_role: Administrationsrolle
37+
groups_claim: Gruppe-Claim (optional)
38+
group_names_pattern: Regex zum Filtern der Namen von Gruppen, die synchronisiert werden sollen
3739
session_check_enabled: Session Check aktivieren
3840
session_check_users_csv: Komma-separierte Liste der Logins mit Session Check (* = alle)
3941
error:

config/locales/en.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ en:
3434
roles_claim_placeholder: roles
3535
access_roles: Space-separated list of authorized roles
3636
admin_role: Administration role
37+
groups_claim: Groups claim (optional)
38+
group_names_pattern: Regex to filter names of groups that should be synchronized
3739
session_check_enabled: Enable session check
3840
session_check_users_csv: Comma-separated list of logins with session check (* = all)
3941
error:

lib/redmine_oidc/settings.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class Settings
3131
roles_claim
3232
access_roles
3333
admin_role
34+
groups_claim
35+
group_names_pattern
3436
session_check_enabled
3537
session_check_users_csv
3638
)
@@ -70,6 +72,14 @@ def to_h
7072
serializable_hash
7173
end
7274

75+
def group_names_regexp
76+
@group_names_regexp ||= Regexp.new(@group_names_pattern || '.*', Regexp::IGNORECASE)
77+
end
78+
79+
def update_groups?
80+
!!@groups_claim
81+
end
82+
7383
def session_check_users
7484
@session_check_users ||= @session_check_users_csv.split(',').map(&:strip)
7585
end

0 commit comments

Comments
 (0)