Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication to Active Directory #875

Closed
jurgele opened this issue Jun 21, 2019 · 10 comments · Fixed by #877
Closed

Authentication to Active Directory #875

jurgele opened this issue Jun 21, 2019 · 10 comments · Fixed by #877
Milestone

Comments

@jurgele
Copy link

jurgele commented Jun 21, 2019

Describe the bug
Some users can authenticate to Active Directory, some can not. They are all in the same OU. Domain Administrator (from a different OU than regular users) can authenticate, but other existing domain users can not. Newly created users (from the same OU than existing users) can authenticate. Also if I copy an existing user that can not authenticate to a new user the new user can authenticate. I checked LDAP messages with MS Message Analyzer on AD server and bind with user credentials is successful for all users, even the ones that can not authenticate.

Local.yaml

security:
    providers:
	chain_provider:
            chain:
                providers: [kimai_ldap]
    firewalls:
	secured_area:
            kimai_ldap: ~

kimai:
    user:
	registration: false
        password_reset: false
    ldap:
	connection:
            host: ad01.is.company.si
            username: CN=Administrator,CN=Users,DC=is,DC=company,DC=si
            password: Password
            accountDomainName: is.company.si
            accountDomainNameShort: COMPANY
            accountFilterFormat: (&(objectClass=Person)(sAMAccountName=%s))
        user:
            baseDn: dc=is,dc=company,dc=si
            filter: (&(objectClass=Person))
            usernameAttribute: samaccountname
            attributes:
                - { ldap_attr: mail, user_method: setEmail }
                - { ldap_attr: displayname, user_method: setAlias }
                - { ldap_attr: samaccountname,  user_method: setUsername }
        role:
            baseDn: dc=is,dc=company,dc=si
            filter: (&(objectClass=group))
            groups:
                - { ldap_value: Leads, role: ROLE_TEAMLEAD }
                - { ldap_value: Domain Admins, role: ROLE_SUPER_ADMIN }
                - { ldap_value: Domain Users, role: ROLE_USER }

Logfile
authentication successful:

doctrine.DEBUG: SELECT t0.username AS username_1, t0.username_canonical AS username_canonical_2, t0.email AS email_3, t0.email_canonical AS email_canonical_4, t0.enabled AS enabled_5, t0.salt AS salt_6, t0.password AS password_7, t0.last_login AS last_login_8, t0.confirmation_token AS confirmation_token_9, t0.password_requested_at AS password_requested_at_10, t0.roles AS roles_11, t0.id AS id_12, t0.alias AS alias_13, t0.registration_date AS registration_date_14, t0.title AS title_15, t0.avatar AS avatar_16, t0.api_token AS api_token_17 FROM kimai2_users t0 WHERE t0.username_canonical = ? LIMIT 1 ["pnep5"] []
app.DEBUG: ldap_search(dc=is,dc=company,dc=si, (&(&(objectClass=Person))(samaccountname=pnep5)), [array]) {"action":"ldap_search","base_dn":"dc=is,dc=company,dc=si","filter":"(&(&(objectClass=Person))(samaccountname=pnep5))","attributes":["+","*"]} []
app.INFO: User pnep5 found on LDAP {"action":"loadUserByUsername","username":"pnep5","result":"found"} []
app.DEBUG: ldap_bind(PNEP5, ****) {"action":"ldap_bind","bindDn":"PNEP5"} []
app.DEBUG: ldap_search(dc=is,dc=company,dc=si, (&(&(objectClass=Person))(samaccountname=PNEP5)), [array]) {"action":"ldap_search","base_dn":"dc=is,dc=company,dc=si","filter":"(&(&(objectClass=Person))(samaccountname=PNEP5))","attributes":["+","*"]} []
app.DEBUG: ldap_search(CN=PNEP5,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si, (objectClass=*), [array]) {"action":"ldap_search","base_dn":"CN=PNEP5,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si","filter":"(objectClass=*)","attributes":["+","*"]} []
app.DEBUG: ldap_search(dc=is,dc=company,dc=si, (&(&(objectClass=group))(member=CN=PNEP5,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si)), [array]) {"action":"ldap_search","base_dn":"dc=is,dc=company,dc=si","filter":"(&(&(objectClass=group))(member=CN=PNEP5,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si))","attributes":["cn","+","*"]} []
security.INFO: User has been authenticated successfully. {"username":"PNEP5"} []

authentication fails:

doctrine.DEBUG: SELECT t0.username AS username_1, t0.username_canonical AS username_canonical_2, t0.email AS email_3, t0.email_canonical AS email_canonical_4, t0.enabled AS enabled_5, t0.salt AS salt_6, t0.password AS password_7, t0.last_login AS last_login_8, t0.confirmation_token AS confirmation_token_9, t0.password_requested_at AS password_requested_at_10, t0.roles AS roles_11, t0.id AS id_12, t0.alias AS alias_13, t0.registration_date AS registration_date_14, t0.title AS title_15, t0.avatar AS avatar_16, t0.api_token AS api_token_17 FROM kimai2_users t0 WHERE t0.username_canonical = ? LIMIT 1 ["namesurname"] []
app.DEBUG: ldap_search(dc=is,dc=company,dc=si, (&(&(objectClass=Person))(samaccountname=namesurname)), [array]) {"action":"ldap_search","base_dn":"dc=is,dc=company,dc=si","filter":"(&(&(objectClass=Person))(samaccountname=namesurname))","attributes":["+","*"]} []
app.INFO: User namesurname found on LDAP {"action":"loadUserByUsername","username":"namesurname","result":"found"} []
app.DEBUG: ldap_bind(NameSurname, ****) {"action":"ldap_bind","bindDn":"NameSurname"} []
app.DEBUG: ldap_search(dc=is,dc=company,dc=si, (&(&(objectClass=Person))(samaccountname=NameSurname)), [array]) {"action":"ldap_search","base_dn":"dc=is,dc=company,dc=si","filter":"(&(&(objectClass=Person))(samaccountname=NameSurname))","attributes":["+","*"]} []
app.DEBUG: ldap_search(CN=Name Surname,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si, (objectClass=*), [array]) {"action":"ldap_search","base_dn":"CN=Name Surname,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si","filter":"(objectClass=*)","attributes":["+","*"]} []
doctrine.DEBUG: SELECT t0.username AS username_1, t0.username_canonical AS username_canonical_2, t0.email AS email_3, t0.email_canonical AS email_canonical_4, t0.enabled AS enabled_5, t0.salt AS salt_6, t0.password AS password_7, t0.last_login AS last_login_8, t0.confirmation_token AS confirmation_token_9, t0.password_requested_at AS password_requested_at_10, t0.roles AS roles_11, t0.id AS id_12, t0.alias AS alias_13, t0.registration_date AS registration_date_14, t0.title AS title_15, t0.avatar AS avatar_16, t0.api_token AS api_token_17 FROM kimai2_users t0 WHERE t0.username_canonical = ? LIMIT 1 ["namesurname"] []
app.DEBUG: ldap_search(dc=is,dc=company,dc=si, (&(&(objectClass=Person))(samaccountname=namesurname)), [array]) {"action":"ldap_search","base_dn":"dc=is,dc=company,dc=si","filter":"(&(&(objectClass=Person))(samaccountname=namesurname))","attributes":["+","*"]} []
app.INFO: User namesurname found on LDAP {"action":"loadUserByUsername","username":"namesurname","result":"found"} []
security.INFO: Authentication request failed. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\BadCredentialsException(code: 0): Bad credentials. at /opt/kimai2/vendor/symfony/security/Core/Authentication/Provider/UserAuthenticationProvider.php:84, Symfony\\Component\\Security\\Core\\Exception\\BadCredentialsException(code: 0): The presented password is invalid. at /opt/kimai2/vendor/symfony/security/Core/Authentication/Provider/DaoAuthenticationProvider.php:58)"} []
security.DEBUG: Authentication failure, redirect triggered. {"failure_path":"fos_user_security_login"} []

Additional context

  • Kimai version: current master branch
  • PHP version: 7.3.6
@kevinpapst
Copy link
Member

kevinpapst commented Jun 21, 2019

I don't know AD and I have no access to one. If authentication works for some users and for others not: find the difference between these accounts.
Are these users new or already existing in Kimai?

This query seems to fail:
app.DEBUG: ldap_search(CN=Name Surname,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si, (objectClass=*), [array]) {"action":"ldap_search","base_dn":"CN=Name Surname,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si","filter":"(objectClass=*)","attributes":["+","*"]} []

Normally it should returns all user attributes for synching. Afterwards the group sync should be executed. But in your failing example another query is executed to find the users DN.

@HeinzWuert
Copy link

Maybe you are using the _ sign in passwords. This may cause an authentification error.
Same with /
Have a try

@jurgele
Copy link
Author

jurgele commented Jun 21, 2019

The problem seems to be in interpreting the results of the search function of the LdapDriver class. When this function is called with parameters $filter = (objectClass=*) and $baseDn = 'CN=Name Surname,OU=COMPANY Users and Groups,DC=is,DC=company,DC=si' it's result is:

  • in case of a user that can successfully authenticate: an array with a single element that is an array containing LDAP attributes of a user that Kimai requires
  • in case of a user that can not authenticate: an array with multiple array elements, element 0 being the array containing LDAP attributes of a user that Kimai requires and the remaining elements being arrays with attributes related to MS Exchange activesync devices

@kevinpapst
Copy link
Member

kevinpapst commented Jun 21, 2019

Hm, then it fails somewhere around here:
https://github.com/kevinpapst/kimai2/blob/master/src/Ldap/LdapManager.php#L77
or here:
https://github.com/kevinpapst/kimai2/blob/master/src/Ldap/LdapManager.php#L132

Can you add something like file_put_contents('/tmp/ldap-login', var_export($entries, true)); before the linked lines and post the result of a successful and failing login here?
If you are more familiar with PHP I wouldn't mind a PR to fix the problem directly...

EDIT: Better add the debug here: https://github.com/kevinpapst/kimai2/blob/master/src/Ldap/LdapManager.php#L67 and
https://github.com/kevinpapst/kimai2/blob/master/src/Ldap/LdapManager.php#L123
Maybe the count is already wrong...

@jurgele
Copy link
Author

jurgele commented Jun 22, 2019

Would it be possible to create a configuration setting in user section for $filter used in
https://github.com/kevinpapst/kimai2/blob/master/src/Ldap/LdapManager.php#L109
The value of '(objectClass=*)' is to generic for AD. I propose a value of '(objectClass=person)' when querying AD but would leave it at '(objectClass=*)' for other LDAP servers to not break compatibility. I modified the $filter directly and have no authentication problems anymore. I can still post the dump of $entries variable if necessary.

@kevinpapst
Copy link
Member

Sure, I can add a new config for that.
Will set the default to (objectClass=*) with the option to overwrite it.
I'll let you know when the PR is there for testing.

@kevinpapst
Copy link
Member

Can you try the branch in #877 with the new config key:

kimai:
    ldap:
        user:
            attributesFilter: (objectClass=person)

Please try it first WITHOUT the new config key, just to make sure the expected error still happens.
Then in step 2 set the config key and let me know if that worked.

And thanks for the investigation 👍

@jurgele
Copy link
Author

jurgele commented Jun 23, 2019

Thank you for doing a wonderful job. It works. I've tested both scenarios.

@kevinpapst
Copy link
Member

Cool. Happy to see it working with AD.

@lock
Copy link

lock bot commented Aug 22, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. If you use Kimai on a daily basis, please consider donating to support further development of Kimai.

@lock lock bot locked and limited conversation to collaborators Aug 22, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants