Skip to content
This repository has been archived by the owner on Feb 14, 2019. It is now read-only.

Commit

Permalink
Adds nested groups support. Thanks Max for suggesting and helping wit…
Browse files Browse the repository at this point in the history
…h this feature.
  • Loading branch information
thorin committed Feb 7, 2012
1 parent 133530f commit 18e5229
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 162 deletions.
65 changes: 48 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ This plugins extends redmine's ldap authentication to perform group
synchronization.
In addition it provides a rake task to perform full user group synchronization.

The following should be noted:
__Features__:

* The plugin has only been tested with Active Directory but should work with
other directories.
* It detects and disables users that have been marked as disabled on LDAP (see
[MS KB Article 305144][uacf] for more details).
* It detects and disables users that have been removed from LDAP.
* Detects and disables users that have been removed from LDAP.
* Detects and disables users that have been marked as disabled on Active
Directory (see [MS KB Article 305144][uacf] for more details).
* Can detect and include nested groups. Upon login the nested groups are
retrieve from disk cache. This cache will only be updated by running the rake
task.

__Remarks__:

* The plugin has only been tested with Active Directory and OpenLDAP but should
work with other directories.
* An user will only be removed from groups that exist on LDAP. This means that
both ldap and non-ldap groups can coexist.
* Deleted groups on LDAP will not be deleted on redmine.

Installation
------------
Installation & Upgrade
----------------------

Follow the plugin installation procedure described at
http://www.redmine.org/wiki/redmine/Plugins
For both upgrade and installation please follow the plugin installation
procedure described at http://www.redmine.org/wiki/redmine/Plugins

Usage
-----
Expand Down Expand Up @@ -48,13 +54,13 @@ able to set for each LDAP authentication.
group's members. Visible if the group membership is __on the group class__.
Eg, `member`.
+ _Memberid attribute (user)_ - The ldap attribute from where to fetch the
user's memberid. This attribute will be used to cross with groups' members.
user's memberid. This attribute must match with the __members attribute__.
Visible if the group membership is __on the group class__. Eg, `dn`.
+ _Groups attribute (user)_ - The ldap attribute from where to fetch the user's
groups. Visible if the group membership is __on the user class__. Eg,
`memberof`.
+ _Groupid attribute (group)_ - The ldap attribute from where to fetch the
group's groupid. This attribute will be used to cross with users' groups.
group's groupid. This attribute must match with the __groups attribute__.
Visible if the group membership is __on the user class__. Eg,
`distinguishedName`.
+ _Groups objectclass_ - The groups object class.
Expand All @@ -63,6 +69,25 @@ able to set for each LDAP authentication.
of the groups that should be imported. Eg, `\.team$`.
+ _Group search filter_ - (optional) An LDAP search filter to be applied
whenever search for groups.
+ _Enable nested groups_ - Enables and specifies how to identify the groups
nesting. When enabled the plugin will look for the groups' parent groups, and
so on, and add those groups to the users.
- **Membership on the parent class** - group membership determined from the
list of groups contained on the parent group.
- **Membership on the member class** - group membership determined from the
list of groups contained on the member group.
+ _Member groups attribute (group)_ - The ldap attribute from where to fetch the
group's member groups. Visible if the nested groups __membership is on the
parent class__. Eg, `member`.
+ _Parent groups attribute (group)_ - The ldap attribute from where to fetch the
group's parent groups. Visible if the nested groups __membership is on the
member class__. Eg, `memberOf`.
+ _Memberid attribute (group)_ - The ldap attribute from where to fetch the
member group's memberid. This attribute must match with the __member groups
attribute__. Eg, `distinguishedName`.
+ _Parentid attribute (group)_ - The ldap attribute from where to fetch the
parent group's id. This attribute must match with the __parent groups
attribute__. Eg, `distinguishedName`.

**Synchronization Actions:**

Expand Down Expand Up @@ -95,14 +120,19 @@ An alternative is to do it periodically with a cron task:
LDAP Compatibility
------------------
### Active Directory
+ _Group name attribute (group)_ = sAMAccountName
+ _Group membership_ = on the group class | {on the user class}
+ _Group name attribute (group)_ = sAMAccountName
+ _Members attribute (group)_ = member
+ _Memberid attribute (user)_ = dn
+ _Groups attribute (user)_ = --- | {memberof}
+ _Groupid attribute (group)_ = --- | {distinguishedName}
+ _Groups objectclass_ = group
+ _Users objectclass_ = user
+ _Nested groups_ = membership on the parent class | {membership on the member class}
+ _Member groups attribute (group)_ - member
+ _Memberid attribute (group)_ - distinguishedName
+ _Parent groups attribute (group)_ - --- | {memberof}
+ _Parentid attribute (group)_ - --- | {distinguishedName}

### OpenDS
+ _Group name attribute (group)_ = cn
Expand All @@ -120,11 +150,12 @@ LDAP Compatibility
+ _Groups objectclass_ = dominoGroup
+ _Users objectclass_ = dominoPerson

### eDirectory / Open LDAP
+ _Group name attribute_ = cn / ??
### Open LDAP (with posixGroups)
+ _Group membership_ = on the group class
+ _Group name attribute_ = cn
+ _Members attribute_ = member
+ _Groups objectclass_ = groupOfNames
+ _Users objectclass_ = person / organizationalPerson
+ _Groups objectclass_ = posixGroup
+ _Users objectclass_ = person

License
-------
Expand Down
100 changes: 63 additions & 37 deletions app/views/settings/_ldap_sync_settings.html.erb
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<%= javascript_tag "function show_options(elem, i) {
$($F(elem) + i).show();
if ($F(elem) == 'on_groups') {
$('on_members' + i).hide();
} else {
$('on_groups' + i).hide();
}
}"
<%= stylesheet_link_tag 'redmine_ldap_sync.css', :plugin => 'redmine_ldap_sync' %>
<%= javascript_tag "function show_options(elem, prefix, i) {
if ($F(elem) != '') $(prefix + $F(elem) + i).show();
$A(elem.options).each(function (option) {
if (option.value != '' && $F(elem) != option.value) {
$(prefix + option.value + i).hide();
}
});
}
"
%>
<% AuthSourceLdap.all.each_with_index do |ldap, i| -%>
<fieldset class="collapsible <%= 'collapsed' if i > 0 %>">
<fieldset class="collapsible <%= 'collapsed' unless ldap.ldapsync_active? %>">
<legend onclick="toggleFieldset(this);"><%= ldap.name %></legend>
<div <%= 'style="display:none"' if i > 0 %>>
<div <%= 'style="display:none"' unless ldap.ldapsync_active? %>>

<p><%= ldap_check_box ldap.name, 'active' %></p>

Expand All @@ -19,52 +21,76 @@
<div>
<p><%= ldap_text_field ldap.name, 'groups_base_dn', :required => true, :size => 50 %></p>

<p><%= ldap_text_field ldap.name, 'attr_groupname', :required => true, :size => 15 %></p>
<p><%= ldap_text_field ldap.name, 'class_user', :default => 'user', :required => true, :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'class_group', :default => 'group', :required => true, :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'groupname_pattern', :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'group_search_filter', :size => 50 %></p>

<p><%= ldap_select ldap.name, 'group_membership', [:on_groups, :on_members], :default => :on_groups,
:onchange => "show_options(this, #{i})",
:onkeyup => "show_options(this, #{i})" %></p>
:onchange => "show_options(this, 'membership_', #{i})",
:onkeyup => "show_options(this, 'membership_', #{i})" %></p>

<div id="on_groups<%= i %>" style="display:none;">
<p><%= ldap_text_field ldap.name, 'attr_member', :default => 'member', :required => true, :size => 15 %></p>
<p><%= ldap_select ldap.name, 'nested_groups', [:on_parents, :on_members], :default => '',
:blank => :option_disabled_nested_groups,
:onchange => "show_options(this, 'nested_', #{i})",
:onkeyup => "show_options(this, 'nested_', #{i})" %></p>

<p><%= ldap_text_field ldap.name, 'attr_user_memberid', :default => 'dn', :required => true, :size => 15 %></p>
</div>
<fieldset class="box">
<legend><%=l :label_attribute_plural %></legend>

<div id="on_members<%= i %>" style="display:none;">
<p><%= ldap_text_field ldap.name, 'attr_user_groups', :default => 'memberof', :required => true, :size => 15 %></p>
<p><%= ldap_text_field ldap.name, 'groupname', :required => true, :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'attr_groupid', :default => 'distinguishedName', :required => true, :size => 15 %></p>
</div>
<div id="membership_on_groups<%= i %>" style="display:none;">
<p><%= ldap_text_field ldap.name, 'member', :default => 'member', :required => true, :size => 15 %></p>

<%= javascript_tag "show_options('#{sanitize_to_id('settings['+ldap.name+'][group_membership]')}', #{i});" %>
<p><%= ldap_text_field ldap.name, 'user_memberid', :default => 'dn', :required => true, :size => 15 %></p>
</div>

<p><%= ldap_text_field ldap.name, 'class_user', :default => 'user', :required => true, :size => 15 %></p>
<div id="membership_on_members<%= i %>" style="display:none;">
<p><%= ldap_text_field ldap.name, 'user_groups', :default => 'memberof', :required => true, :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'class_group', :default => 'group', :required => true, :size => 15 %></p>
<p><%= ldap_text_field ldap.name, 'groupid', :default => 'distinguishedName', :required => true, :size => 15 %></p>
</div>

<p><%= ldap_text_field ldap.name, 'groupname_pattern', :size => 15 %></p>
<%= javascript_tag "show_options('#{sanitize_to_id('settings['+ldap.name+'][group_membership]')}', 'membership_', #{i});" %>

<div id="nested_on_members<%= i %>" style="display:none;">
<p><%= ldap_text_field ldap.name, 'parent_group', :required => true, :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'group_parentid', :required => true, :size => 15 %></p>
</div>

<div id="nested_on_parents<%= i %>" style="display:none;">
<p><%= ldap_text_field ldap.name, 'member_group', :required => true, :size => 15 %></p>

<p><%= ldap_text_field ldap.name, 'group_memberid', :required => true, :size => 15 %></p>
</div>

<%= javascript_tag "show_options('#{sanitize_to_id('settings['+ldap.name+'][nested_groups]')}', 'nested_', #{i});" %>
</fieldset>

<p><%= ldap_text_field ldap.name, 'group_search_filter', :size => 50 %></p>
</div>
</fieldset>

<fieldset class="collapsible collapsed">
<legend onclick="toggleFieldset(this);"><%=l :text_synchronization_actions %></legend>
<div style="display: none;">
<p><%= ldap_text_field ldap.name, 'required_group' %></p>
<fieldset class="collapsible collapsed">
<legend onclick="toggleFieldset(this);"><%=l :text_synchronization_actions %></legend>
<div style="display: none;">
<p><%= ldap_text_field ldap.name, 'required_group' %></p>

<p><%= ldap_text_field ldap.name, 'fixed_group', :size => 15 %></p>
<p><%= ldap_text_field ldap.name, 'fixed_group', :size => 15 %></p>

<p><%= ldap_check_box ldap.name, 'create_groups', :default => true %></p>
<p><%= ldap_check_box ldap.name, 'create_groups', :default => true %></p>

<p><%= ldap_check_box ldap.name, 'create_users', :default => true %></p>
<p><%= ldap_check_box ldap.name, 'create_users', :default => true %></p>

<p><%= ldap_check_box ldap.name, 'sync_user_attributes' %></p>
<p><%= ldap_check_box ldap.name, 'sync_user_attributes' %></p>

<p><%= ldap_multiselect ldap.name, 'attributes_to_sync', ['firstname', 'lastname', 'mail'], :size => 15 %></p>
</div>
</fieldset>
<p><%= ldap_multiselect ldap.name, 'attributes_to_sync', ['firstname', 'lastname', 'mail'], :size => 15 %></p>
</div>
</fieldset>

</div>
</fieldset>
Expand Down
1 change: 1 addition & 0 deletions assets/stylesheets/redmine_ldap_sync.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fieldset.box legend { padding-left: 2px; background-image: none; cursor: default; }
21 changes: 14 additions & 7 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
de:
field_redmine_ldap_sync_active: "Aktiv"
field_redmine_ldap_sync_groups_base_dn: "Gruppen DN"
field_redmine_ldap_sync_attr_groupname: "Gruppenname"
field_redmine_ldap_sync_group_membership: "Group membership"
field_redmine_ldap_sync_attr_member: "Gruppenteilnehmerattribute"
field_redmine_ldap_sync_attr_member_group: "Members group attribute (group)"
field_redmine_ldap_sync_attr_user_memberid: "Memberid attribute (user)"
field_redmine_ldap_sync_attr_user_groups: "Groups attribute (user)"
field_redmine_ldap_sync_attr_groupid: "Groupid attribute (group)"
field_redmine_ldap_sync_nested_groups: "Enable nested groups"
field_redmine_ldap_sync_class_user: "Benutzerobjektklasse"
field_redmine_ldap_sync_class_group: "Gruppenobjektklasse"

field_redmine_ldap_sync_groupname_pattern: "Regular Expression für den Gruppenfilter"
field_redmine_ldap_sync_group_search_filter: "Gruppensuchfilfer"

field_redmine_ldap_sync_nested_groups: "Enable nested groups"
field_redmine_ldap_sync_groupname: "Gruppenname"
field_redmine_ldap_sync_member: "Gruppenteilnehmer"
field_redmine_ldap_sync_member_group: "Member groups (group)"
field_redmine_ldap_sync_parent_group: "Parent groups (group)"
field_redmine_ldap_sync_user_memberid: "Memberid (user)"
field_redmine_ldap_sync_group_memberid: "Memberid (group)"
field_redmine_ldap_sync_group_parentid: "Parentid (group)"
field_redmine_ldap_sync_user_groups: "Groups (user)"
field_redmine_ldap_sync_groupid: "Groupid (group)"

field_redmine_ldap_sync_required_group: "Benutzer müssen Gruppenteilnehmer sein"
field_redmine_ldap_sync_fixed_group: "Benutzer zur Gruppe hinzufügen"
Expand All @@ -32,3 +35,7 @@ de:

option_redmine_ldap_sync_group_membership_on_groups: "On the group class"
option_redmine_ldap_sync_group_membership_on_members: "On the user class"

option_disabled_nested_groups: "Disabled"
option_redmine_ldap_sync_nested_groups_on_parents: "Membership on the parent class"
option_redmine_ldap_sync_nested_groups_on_members: "Membership on the member class"
21 changes: 14 additions & 7 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
en:
field_redmine_ldap_sync_active: "Active"
field_redmine_ldap_sync_groups_base_dn: "Groups base DN"
field_redmine_ldap_sync_attr_groupname: "Group name attribute (group)"
field_redmine_ldap_sync_group_membership: "Group membership"
field_redmine_ldap_sync_attr_member: "Members attribute (group)"
field_redmine_ldap_sync_attr_member_group: "Members group attribute (group)"
field_redmine_ldap_sync_attr_user_memberid: "Memberid attribute (user)"
field_redmine_ldap_sync_attr_user_groups: "Groups attribute (user)"
field_redmine_ldap_sync_attr_groupid: "Groupid attribute (group)"
field_redmine_ldap_sync_nested_groups: "Enable nested groups"
field_redmine_ldap_sync_class_user: "Users objectclass"
field_redmine_ldap_sync_class_group: "Groups objectclass"

field_redmine_ldap_sync_groupname_pattern: "Group name pattern"
field_redmine_ldap_sync_group_search_filter: "Group search filter"

field_redmine_ldap_sync_nested_groups: "Enable nested groups"
field_redmine_ldap_sync_groupname: "Group name (group)"
field_redmine_ldap_sync_member: "Member users (group)"
field_redmine_ldap_sync_member_group: "Member groups (group)"
field_redmine_ldap_sync_parent_group: "Parent groups (group)"
field_redmine_ldap_sync_user_memberid: "Memberid (user)"
field_redmine_ldap_sync_group_memberid: "Memberid (group)"
field_redmine_ldap_sync_group_parentid: "Parentid (group)"
field_redmine_ldap_sync_user_groups: "Groups (user)"
field_redmine_ldap_sync_groupid: "Groupid (group)"

field_redmine_ldap_sync_required_group: "Users must be members of"
field_redmine_ldap_sync_fixed_group: "Add users to group"
Expand All @@ -32,3 +35,7 @@ en:

option_redmine_ldap_sync_group_membership_on_groups: "On the group class"
option_redmine_ldap_sync_group_membership_on_members: "On the user class"

option_disabled_nested_groups: "Disabled"
option_redmine_ldap_sync_nested_groups_on_parents: "Membership on the parent class"
option_redmine_ldap_sync_nested_groups_on_members: "Membership on the member class"
21 changes: 14 additions & 7 deletions config/locales/es.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
es:
field_redmine_ldap_sync_active: "Activo"
field_redmine_ldap_sync_groups_base_dn: "Base DN de grupos"
field_redmine_ldap_sync_attr_groupname: "Nombre del grupo"
field_redmine_ldap_sync_group_membership: "Pertencia al grupo"
field_redmine_ldap_sync_attr_member: "Atributo membros (grupo)"
field_redmine_ldap_sync_attr_member_group: "Members group attribute (group)"
field_redmine_ldap_sync_attr_user_memberid: "Atributo memberid (usuario)"
field_redmine_ldap_sync_attr_user_groups: "Atributo grupos (usuario)"
field_redmine_ldap_sync_attr_groupid: "Atributo groupid (grupo)"
field_redmine_ldap_sync_nested_groups: "Habilitar grupos anidados"
field_redmine_ldap_sync_class_user: "Objectclass de usuarios"
field_redmine_ldap_sync_class_group: "Objectclass de grupos"

field_redmine_ldap_sync_groupname_pattern: "Filtro regex de grupos"
field_redmine_ldap_sync_group_search_filter: "Filtro de búsqueda de grupos"

field_redmine_ldap_sync_nested_groups: "Enable nested groups"
field_redmine_ldap_sync_groupname: "Nombre del grupo"
field_redmine_ldap_sync_member: "Miembros (grupo)"
field_redmine_ldap_sync_member_group: "Grupos miembros (grupo)"
field_redmine_ldap_sync_parent_group: "Grupos padres (grupo)"
field_redmine_ldap_sync_user_memberid: "Memberid (user)"
field_redmine_ldap_sync_group_memberid: "Memberid (grupo)"
field_redmine_ldap_sync_group_parentid: "Parentid (grupo)"
field_redmine_ldap_sync_user_groups: "Grupos (usuario)"
field_redmine_ldap_sync_groupid: "Groupid (grupo)"

field_redmine_ldap_sync_required_group: "Usuarios deben ser miembros de"
field_redmine_ldap_sync_fixed_group: "Añadir usuarios al grupo"
Expand All @@ -32,3 +35,7 @@ es:

option_redmine_ldap_sync_group_membership_on_groups: "En la clase grupo"
option_redmine_ldap_sync_group_membership_on_members: "En la clase usuario"

option_disabled_nested_groups: "Deshabilitado"
option_redmine_ldap_sync_nested_groups_on_parents: "Pertenencia en el padre"
option_redmine_ldap_sync_nested_groups_on_members: "Pertenencia en el miembro"
Loading

0 comments on commit 18e5229

Please sign in to comment.