Arcath / Adauth

A Ruby interface for Microsoft's Active Directory based off ruby-net-ldap
http://adauth.arcath.net
MIT License
174 stars 29 forks source link

Users with large numbers of group memberships experience slow login times using TLS #48

Open hermiti opened 10 years ago

hermiti commented 10 years ago

There is an issue where if a user is associated with a large number of groups the time required to login when simple_tls is enabled is greatly increased. The average response time for a user with 100 group memberships is around 1 second when simple_tls is not in use. However, when it is in use the time escalates to 20+ seconds for a login.

I believe this is caused by the amount of repeated queries that is done combined with the overhead of simple_tls.

For each group membership there are an additional four queries -- two group queries and two user queries. A user with 100 group memberships generate 500 queries. The interesting thing is that the additional four queries seem irrelevant, the first query is against a null user/group. The second set of queries is against a seemingly random user that is a member of the group.

Searching for all "(objectClass=user)" where sAMAccountName = user_a
Connecting to AD as "ad_user"
Searching for all "(objectClass=group)" where name = group_a
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = user_b
Searching for all "(objectClass=group)" where sAMAccountName = user_b
Searching for all "(objectClass=group)" where name = group_b
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = user_c
Searching for all "(objectClass=group)" where sAMAccountName = user_c
...
(500 lines)
hermiti commented 10 years ago

If I bypass the allowed_to_login method which checks all of the group and ous then the delay is gone. I would think that it would be better to check to see if the user actually needs the application to verify if it is needed to traverse the users group and uo assignments prior to doing so.

The original code:

def self.allowed_to_login(user)
  (allowed_from_arrays(@config.allowed_groups, @config.denied_groups, user.cn_groups_nested) && allowed_from_arrays(@config.allowed_ous, @config.denied_ous, user.dn_ous))
end

Here would be an alternative:

def self.allowed_to_login(user)
  #Only inspect groups and organizations if required
  (((@config.allowed_groups.blank? && @config.denied_groups.blank?) || allowed_from_arrays(@config.allowed_groups, @config.denied_groups, user.cn_groups_nested)) && ((@config.allowed_ous.blank? && @config.denied_ous.blank?) || allowed_from_arrays(@config.allowed_ous, @config.denied_ous, user.dn_ous)))
end

On a side note allowed_ous and denied_ous are not mentioned in the default config. Cn_groups_nested an dn_ous need some love to help with this issue for those users that do have to verify group and ou membership.

Arcath commented 10 years ago

Will look into tidying this up ASAP