3liz / lizmap-web-client

Transfer a QGIS project on a server, Lizmap is providing the web interface to browse it
https://www.lizmap.com
Mozilla Public License 2.0
261 stars 144 forks source link

ldap for authentication #110

Closed tbeg closed 7 years ago

tbeg commented 9 years ago

Hi,

I was able to switch from sqlite to postgresql for lizamp authentication backend reading the jelix docs. Very handy! From the docs I also noticed jelix support for ldap although it's not so obvious to me how to implement it. Is this sth. lizmap would support? Can I easily plugin the ldap driver to authenticate my lizmap users? What is involved to implement it?

Thanks and regards,

Thomas

mdouchin commented 9 years ago

Hi

@laurentj has coded a new driver called ldapdao to handle users from a LDAP Server, but still uses a PostGreSQL or sqlite backend to store Lizmap-specific rights. This subject is more related to https://github.com/jelix/jelix . This driver would be available in a next lizmap version

laurentj commented 9 years ago

I opened issue jelix/jelix#193 ;-)

laurentj commented 9 years ago

The plugin is here (embedded into a module) : https://github.com/jelix/ldapdao-module

If you have some difficulties to configure it, ask your questions and so I could improve the documentation.

pieri70 commented 7 years ago

Hello I read the lizmap documentation

If I'm not wrong Lizmap needs a new entire postgresql database to store credentials and other things it needs Is it so? Is it possible to use a given schema in a existing database to store credentials instead of a new database?

Then, is it possible to map the user that is editing a layer instead of the user saved in the connection string on the Qgis project file? As asked here? https://github.com/3liz/lizmap-web-client/issues/457

Thanks Pietro

mdouchin commented 7 years ago

My answers:

No, lizmap does not need a new different database. It uses the database defined in the lizmap/var/config/profiles.ini.php when Lizmap is installed. You can even create the tables manually (or import them from a previous Lizmap installation) in a database, and use them in Lizmap.

NB: I wont answer here to the last question linked to #457 in this issue, to avoid cross-posting.

pieri70 commented 7 years ago

Thanks @mdouchin in the guide I don't see a schema variable

If I set schema="myschema" does lizmap use that for its tables? Otherwise, which tables should I create to serve Lizmap with the correct space to store its informations?

;Example of different driver (e.g PostgreSQL) [jdb:jauth] driver="pgsql" database="name_of_database" host="localhost" user="Admin_user_postgreSQL" password="put_here_the_password"

[jdb:lizlog] driver="pgsql" database="name_of_database" host="localhost" user="Admin_user_postgreSQL" password="put_here_the_password"

mdouchin commented 7 years ago

You could use a search_path variable with a list of schemas separated by comma.

search_path="myschema,public"

You can see which tables to create here: https://github.com/3liz/lizmap-web-client/blob/master/lib/jelix-modules/jauthdb/install/install_jauth.schema.pgsql.sql and here https://github.com/3liz/lizmap-web-client/blob/master/lib/jelix-modules/jacl2db/install/install_jacl2.schema.pgsql.sql

(replace %%PREFIX%% with your schema)

josemvm commented 7 years ago

hi @laurentj @mdouchin

i intend to try the ldap authentication

INSTALLATION

my questions:

thank you in advance

laurentj commented 7 years ago

folder where i put the plugin: lib, jelix, jelix/plugins or jelix-modules

You must put nothing in lib/ and its child directories, else you'll have difficulties to upgrade Lizmap. Everything you want to add should be stored inside lizmap/.

Copy the ldapdao directories (from https://github.com/jelix/ldapdao-module ) into lizmap/lizmap-modules/.

declare the module into the configuration of your application --> localconfig.ini.php

Yes.

declare the path to ldapdao-module in the modulesPath --> mainconfig.ini.php

do nothing if you install it into lizmap/lizmap-modules/.

modules are required: jacl2, jauth, jauthdb --> nothing to do ?

No, it is already activated for Lizmap (see the mainconfig.ini.php)

php yourapp/cmd.php install --> php lizmap/install/installer.php

Yes. or php lizmap/cmd.php install. This is the same thing.

josemvm commented 7 years ago

sorry for the noise...

now i'm trying with a fresh install (master versions), step by step. i'm using the authldap.coord.ini.php file without any changes and i don't get messages related to ldap server (only "Failed to login")

what is missing? something in profiles.ini.php file?

[jdb]

; name of the default profile to use for any connection default=jauth jacl2_profile=jauth

[jdb:jauth] driver=sqlite3 database="var:db/jauth.db"

thanks again

laurentj commented 7 years ago

In fact the var/config/admin/config.ini.php and var/config/index/config.ini.php redefine the path of the auth config. So you should change also them :

[coordplugins]
auth="authldap.coord.ini.php"

Other changes to do into authldap.coord.ini.php

after_login = "view~default:index"
persistant_crypt_key="change it to any other sentence, it is used to encrypt cookies" 

Be careful, you have two "searchGroupFilter=", and the last is empty : it will override the value that is above it.

For other ldap values (uidProperty, bindUserDN, searchBaseDN, searchUserFilter, searchUserListFilter, searchUserListReturnsUser, searchUserListUserUidAttribute, searchAttributes, searchGroupFilter, searchGroupProperty), be sure it corresponds to your ldap structure. Check them with the ldap administrator. Anyway I can't help you on these values as I'm not very familiar with ldap.

Be sure also to remove all files from temp/lizmap/. Sometimes temporary files are not all updated when the configuration changes.

josemvm commented 7 years ago

hi @laurentj

thanks for your helps

after following all your instructions, i'm at this point now:

error [612] ldap extension unloaded /var/www/html/lizmap-web-client-master/lizmap/lizmap-modules/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php 23

thanks again

josemvm commented 7 years ago

hi @laurentj

i'm familiar with ldap server, i have some applications to do ldap authentication, like zimbra mail server, openfire messenger server, printing systems, etc.

i just need your help to get to the point of communication between the LWC and the ldap server

josemvm commented 7 years ago

fter installing the php ldap module, i think i'm at that point now

warning ldap_bind(): Unable to bind to server: Invalid credentials /var/www/html/lizmap-web-client-master/lizmap/lizmap-modules/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php 169

josemvm commented 7 years ago

hi @laurentj

i can already get ldap authentication with: bindUserDN="uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt" --> DN: uid=foo,ou=Users,dc=cmarl,dc=pt

but my openldap structure is different than expected: bindUserDN="cn=??????,ou=users,dc=cmarl,dc=pt" --> DN: cn=foo xpto,ou=Users,dc=cmarl,dc=pt

could you please contemplate this kind of structure in your module code (authldap.coord.ini.php)?

laurentj commented 7 years ago

could you please contemplate this kind of structure in your module code

Why ? just modify the parameter bindUserDN to match your ldap structure...

bindUserDN="cn=%%USERNAME%% xpto,ou=users,dc=cmarl,dc=pt"

No ?

Or perhaps i didn't understand what do you expect..

josemvm commented 7 years ago

sorry, maybe i did not explain myself well


in openldap users structure there are 3 default users (admin, Administrator and nobody) for which the distinguished name (DN) is composed as follows:

DN: uid=admin,ou=Users,dc=cmarl,dc=pt

for these users i can get ldap authentication, using:

uidProperty=uid
bindUserDN="uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt"

for all other users the DN is composed as follows:

DN: cn=Jose Macau,ou=Users,dc=cmarl,dc=pt

however, the user to login has to provide his uid, not his cn

for these users i can't get ldap authentication, using as you suggest:

uidProperty=uid
bindUserDN="**cn=%%USERNAME%%**,ou=users,dc=cmarl,dc=pt"
warning ldap_bind(): Unable to bind to server: Invalid credentials      /var/www/html/lizmap-web-client-master/lizmap/lizmap-modules/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php 169
laurentj commented 7 years ago

@josemvm Ok, If I understood well, the bindUserDN is different according to the type of user. So, a solution could be the possibility to indicate several bindUserDN values, and then the connector will try each one until a success. Right ?

For example, we could have this possibility in the configuration :

bindUserDN[]="uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt"
bindUserDN[]="cn=%%USERNAME%%,ou=users,dc=cmarl,dc=pt"

(note the brackets [], that indicates an item of a list)

Then, when the connector will try to connect, it will try first with uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt, and if it fails, it will try with cn=%%USERNAME%%,ou=users,dc=cmarl,dc=pt and so on...

If I implement such behavior, will it fix your issue?

josemvm commented 7 years ago

yes, theoretically correct

if you can provide me same code, i'll test and send you feedback before you implement it

laurentj commented 7 years ago

@josemvm I pushed the new implementation

Get the source code of ldapdao.auth.php here, and set the configuration like this:

bindUserDN[]="cn=%%USERNAME%%,ou=users,dc=cmarl,dc=pt"
bindUserDN[]="uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt"

The order is important. Probably the first one should correspond to the most of users, the last one to specific users, in order to limit the number of failed tries during the life of the app.

tell me if it works :-)

josemvm commented 7 years ago

hi @laurentj

i'll give you soon a cleaner feedback

josemvm commented 7 years ago

hi @laurentj

first time: login with user uid (Administrator) and create the user in jauth.db

lizmap-web-client-master/lizmap/var/log/error.log warning [2] usort() expects parameter 2 to be a valid callback, function 'mainViewItemSort' not found or invalid function name /var/www/html/lizmap-web-client-master/lizmap/modules/view/zones/main_view.zone.php 138 (message number 1)

next time: login ok the same warning (log 1)

conclusion: the module works without any problems as usual


first time: login with user cn (Jose Macau) and create the user in jauth.db the same warning (log 1)

next time: never connect again with user cn warning [2] SQLite3::exec(): UNIQUE constraint failed: jlx_user.usr_login /var/www/html/lizmap-web-client-master/lib/jelix/plugins/db/sqlite3/sqlite3.dbconnection.php 120

error [403] invalid query (UNIQUE constraint failed: jlx_user.usr_login (INSERT INTO jlx_user (usr_login,usr_email,usr_password,firstname,lastname,organization,phonenumber,street,postcode,city,country,comment) VALUES ('ze_macau', 'mymail@domain.pt', 'no password', 'José', 'Macau', NULL, NULL, NULL, NULL, NULL, NULL, NULL))) /var/www/html/lizmap-web-client-master/lib/jelix/plugins/db/sqlite3/sqlite3.dbconnection.php 123

reflection 1: it seems to me that it try to create again the same user with the same primary key and fail...

if i try to login with uid (ze_macau) as desired and expected, "failed to login" without errors log

reflection 2: it seems to me that it need a function that could extract the user cn from corresponding user uid using _bindLdapAdminUser function, and assign this value to the bindUserDN and allow user to login with it.

but i don't know, honestly i have no programming skills.


thanks again for your work

laurentj commented 7 years ago

Sorry for the delay.

I will take a look on this issue next days.

josemvm commented 7 years ago

hi @laurentj

everything is quiet, it's not an urgent matter

thanks

laurentj commented 7 years ago

Hi @josemvm

I try to understand what is the problem.

When it is uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt ̀, you must enter "ze_macau" in the login form. But when it is cn=%%USERNAME%%,ou=users,dc=cmarl,dc=pt, you must enter "Jose Macau". Right?

And in the configuration searchAttributes, you have a thing like "uid:login", right?

If yes at both, then I think I found the bug. When you enter "Jose Macau", the plugin searches the user into the database, by using the given login "Jose Macau". It doesn't find it, so it creates a new record. But before that, it retrieves the user's attributes, and because of the value of searchAttributes, the login is changed from "Jose Macau" to "ze_macau". As the login is the primary key in the table, the first time it works, the record is created, but the second time it fails. So in fact, the plugin should retrieve attributes first before checking the existance of the user in the database, to search with the uid, not the given cn.

josemvm commented 7 years ago

Hi @laurentj,

And in the configuration searchAttributes, you have a thing like "uid:login", right?

Yes.

For uid='ze_macau' and cn='Jose Macau' my bindUserDN="cn=Jose Macau,ou=users,dc=cmarl,dc=pt", it's the way that my ldap server will do the authentication. But i want to give it the uid(ze_macau) and not the cn(Jose Macau), so the plugin should retrieve attributes first, as you said and then by the uid find the cn and try the authentication.

Thanks in advance.

laurentj commented 7 years ago

ok, thank you, that's clear for me now :-)

mdouchin commented 7 years ago

Hi @laurentj and @josemvm I have had the same issue with a client using ActiveDirectory.

I propose this diff wich is a workaround. @josemvm Can you try it ? -> updated diff on last post

mdouchin commented 7 years ago

With this patch, I have used the configurations:

searchAttributes="cn,distinguishedName,name"

to be able to get the distinguishedName from the LDAP server

NB : context explained here http://stackoverflow.com/questions/8324785/authenticating-user-with-ldap-from-php-with-only-samaccountname-and-password

There is no way to connect via sAMAccountName and password, but we need to use distinguishedName

mdouchin commented 7 years ago

Updated diff after some fixes

diff --git a/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php b/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php
index 56552c6..3028015 100644
--- a/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php
+++ b/ldapdao/plugins/auth/ldapdao/ldapdao.auth.php
@@ -180,6 +180,24 @@ class ldapdaoAuthDriver extends jAuthDriverBase implements jIAuthDriver {
         }
         ldap_close($connect);

+        // First pass with direct login has not worked
+        // Connect as admin, to get more information on the user
+        // This is necessary in ActiveDirectory to get the user by distinguishedName
+        if(!$bind){
+            $aconnect = $this->_bindLdapAdminUser();
+
+            // Create user instance, but do not insert it into database
+            $user = $this->createUserObject($login, '');
+
+            //get ldap user infos: name, email etc... 
+            if($checkUser = $this->searchLdapUserAttributes($aconnect, $login, $user)){
+                $connect = $this->_getLinkId();
+                $bind = @ldap_bind($connect, $user->distinguishedName, $password);
+                ldap_close($connect);
+            }
+            ldap_close($aconnect);
+        }
+
         if (!$bind) {
             jLog::log('ldapdao: cannot bind to any configured path with the login '.$login, 'auth');
             return false;
josemvm commented 7 years ago

hi @mdouchin

i tried to use your patch without your configurations

searchAttributes="cn,distinguishedName,name"

but in my case it doesn't work, the problem remains the same


my ldap DN = cn,ou,dc,dc

example:

For uid='ze_macau' and cn='Jose Macau' my bindUserDN="cn=Jose Macau,ou=users,dc=cmarl,dc=pt", it's the way that my ldap server will do the authentication. But i want to give it the uid(ze_macau) and not the cn(Jose Macau), so the plugin should retrieve attributes first, as you said and then by the uid find the cn and try the authentication.

@laurentj and @mdouchin i send you a java code file which is used to my ldap authentication, so please take a look at it

@mdouchin please check your mail box and share the file with @laurentj

mdouchin commented 7 years ago

@josemvm please replace in my 'patch' the distinguishedName by bindUserDN

and use the following parameter in the config ( see the use of all fields required to match ldap parameters and lizmap field names, and at the end the bindUserDN )

searchAttributes="uid:login,givenName:firstname,sn:lastname,mail:email,bindUserDN,name"

It seems our server need the full DN to authenticate a user:

And they cannot use something simple like uid=%%USERNAME%%,ou=users,dc=cmarl,dc=pt

so we need to get the data from the LDAP server for the user, including bindUserDN (or distinguishedName in my case )

Please test it and report This should work but obviously we should add a property in LDAP configuration "Use this property as DN to authenticate for the user if the server forces the use of full DN for authentication"

josemvm commented 7 years ago

@mdouchin the problem remains

first time: only login with user cn (Jose Macau) and create the user in jauth.db, but not with uid='ze_macau' (as expected)

next time: never connect again with user cn (Jose Macau) and also not with uid='ze_macau'

mdouchin commented 7 years ago

@josemvm you should not log in with the cn but with the uid. Please post a Gist (or a pastebin) where we can see the content of the authldap config file (REMOVE THE PASSWORD)

@laurentj PR with new option here https://github.com/jelix/ldapdao-module/pull/3

laurentj commented 7 years ago

The solution that I will implement:

I'm not sure if trying several different bindUserDN is needed (for example, should we need a bindUserDN filter for each searchUserFilter?)

What do you think about it ?

josemvm commented 7 years ago

hi @laurentj and @mdouchin thanks for your efforts.

i think you are rigth @laurentj , but what do you want to say with the

searchUserIdAttribute

i can't find it in authldap.coord.ini.php! if i add it searchUserIdAttribute="cn"; how can i then use it on binUserDn?

eg: searchUserIdAttribute="cn"; bindUserDN[]="cn=%%searchUserIdAttribute%%,ou=users,dc=cmarl,dc=pt"

something like that?...

laurentj commented 7 years ago

i can't find it in authldap.coord.ini.php!

yes, because it does not exist yet ;-) But in fact, it is similar to the uidProperty property...

However, I will improve the solution like this, to ease and support requirements from both, you @josemvm and @mdouchin.

a bindUserDN value may follow this syntax :

@josemvm, the configuration might become:

searchUserFilter="(&(objectClass=*)(cn=%%LOGIN%%))"
bindUserDN[]="cn=?,ou=users,dc=cmarl,dc=pt"
bindUserDN[]="uid=?,ou=users,dc=cmarl,dc=pt"

So, the result has the cn that is equals to the given login. The plugin will then try the first DN with the cn attribute value of the result, and if it fails, it will try the second DN with the uid attribute value of the result.

@mdouchin , the configuration might become:

searchUserFilter="(sAMAccountName=%%LOGIN%%)"
bindUserDN="$dn"

In this case, to bind to the ldap server, it will take the dn attribute value from the result that has the given login as a value of the attribute sAMAccountName.

Is it better?

josemvm commented 7 years ago

ok @laurentj

it seems perfect to me, go ahead!

thanks

laurentj commented 7 years ago

I'm currently implementing the solution :-)

josemvm commented 7 years ago

nice! have a good day at work :-)

laurentj commented 7 years ago

I made the changes, but not tested yet. Can you verify that it works for you?

Note that i made a little modification into the specification: in bindUserDN, use %?% instead of ?.

josemvm commented 7 years ago

hi @laurentj

excellent!!!

now, i can get authentication from my ldap with

searchUserFilter="(&(objectClass=*)(cn=%%LOGIN%%))"

for cn (just an experiment)

or with

searchUserFilter="(&(objectClass=*)(uid=%%LOGIN%%))"

for uid, as i intend!


and much more: i can also use simultaneously 2 bindUserDN (as expected, according to your code)

bindUserDN[]="cn=%%LOGIN%%,ou=users,dc=cmarl,dc=pt" bindUserDN[]="uid=%%LOGIN%%,ou=users,dc=cmarl,dc=pt"


i also tried configured groups rights into LWC but without success...

my DN (group): cn=informatica,ou=Groups,dc=cmarl,dc=pt i created a group as informatica name in LWC and i'm using

searchGroupFilter="(&(objectClass=*)(cn=*)(memberUid=%%LOGIN%%))" searchGroupProperty="cn"

but the group is not assigned to the user (memberUid)


thank you so much @laurentj :-)

josemvm commented 7 years ago

hi @laurentj

i think that the plugin needs something like this for the groups:

searchBaseDN="ou=Groups,dc=cmarl,dc=pt"

what do you think about it?

laurentj commented 7 years ago

@josemvm you mean, you would like a searchGroupBaseDN property to search groups, like searchBaseDN for users?

josemvm commented 7 years ago

yes @laurentj

because the user properties have no field with a memberUid the memberUid is present only in the goup properties, like this:

DN: cn=informatica,ou=Groups,dc=cmarl,dc=pt memberuid

so, i think that the plugin must search the user groups from the groups (ou=Groups")... and in this case the plugin needs a searchBaseDN...

thanks

laurentj commented 7 years ago

@josemvm ok. I made this change. You now have a searchGroupBaseDN property. I also renamed searchBaseDN to searchUserBaseDN.

And ldap connection settings have been moved to profiles.ini.php. See the documentation into README.md.

josemvm commented 7 years ago

many thanks @laurentj

i will try ASAP

josemvm commented 7 years ago

hi @laurentj

should i set these 2 profiles in profiles.ini.php file?

[jdb:myldapdao] driver="mysqli" host= "localhost" or ldap server ip ? database="userdb" user= "admin" password="jelix" or "admin" ? persistent= on force_encoding = on

[ldap:myldapdao] hostname=10.27.3.5 or ldap://10.27.3.5 ? port=389 adminUserDn="cn=Manager,dc=cmarl,dc=pt" adminUserPassword="xxxxxxxx"


and in authldap.coord.ini.php file?

; profile to use for jDb ; profile = "jauth"

; profile to use for ldap profile="myldapdao" ldapprofile="myldapdao"


with these options i get a blank page without log errors...

laurentj commented 7 years ago

For profile, it if worked before, you don't have to change anything, and you don't have to set a new profile jdb:myldapdao.

However, you should create a profile for ldap settings. You just have to move ldap properties like "hostname", "port", ldapAdminUserDn" and "ldapAdminPassword" to profiles.ini.php:

[ldap:myldapdao]
hostname=10.27.3.5
port=389
adminUserDn="cn=Manager,dc=cmarl,dc=pt"
adminUserPassword="xxxxxxxx"

and set ldapprofile="myldapdao" into authldap.coord.ini.php

josemvm commented 7 years ago

hi @laurentj

to debug the problem, i came back at your commit https://github.com/jelix/ldapdao-module/commit/24af788bf281a17dc63a906d52de6cb6b5a298a7

and after this commit and with the due changes/adaptations into authldap.coord.ini.php file the plugin stopped working.

should the plugin be working after this commit? i think so but it doesn't.