3 * FusionForge system users integration
5 * Copyright 2004, Christian Bayle
6 * Copyright 2010, Roland Mas
8 * This file is part of FusionForge. FusionForge is free software;
9 * you can redistribute it and/or modify it under the terms of the
10 * GNU General Public License as published by the Free Software
11 * Foundation; either version 2 of the Licence, or (at your option)
14 * FusionForge is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 require_once $gfcommon.'include/account.php';
25 require_once $gfcommon.'include/system/UNIX.class.php';
27 class LDAP extends UNIX {
42 * asciize() - Replace non-ascii characters with question marks
44 * LDAP expects utf-8 encoded character string. Since we cannot
45 * know which encoding 8-bit characters in database use, we
46 * just replace them with question marks.
48 * @param string UTF-8 encoded character string.
49 * @return string which contains only ascii characters
51 function asciize($str) {
53 // LDAP don't allow empty strings for some attributes
57 return preg_replace("/[\x80-\xff]/","?",$str);
61 * Wrappers for PHP LDAP functions
65 * gfLdapConnect() - Connect to the LDAP server
67 * @returns true on success/false on error
70 function gfLdapConnect() {
76 $ldap_conn = @ldap_connect(forge_get_config('ldap_host'),forge_get_config('ldap_port'));
78 $this->setError('Error: Cannot connect to LDAP server<br />');
81 if (forge_get_config('ldap_version')) {
82 ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, forge_get_config('ldap_version'));
84 ldap_bind($ldap_conn,forge_get_config('ldap_bind_dn'),forge_get_config('ldap_password'));
90 * gfLdapAdd() - Wrapper for ldap_add()
96 function gfLdapAdd($dn, $entry) {
98 return @ldap_add($ldap_conn,$dn,$entry);
102 * gfLdapDelete() - Wrapper for ldap_delete()
107 function gfLdapDelete($dn) {
109 return @ldap_delete($ldap_conn,$dn);
113 * gfLdapModify() - Wrapper for ldap_modify()
116 * @param string entry
119 function gfLdapModify($dn,$entry) {
121 return @ldap_modify($ldap_conn,$dn,$entry);
125 * gfLdapModifyIfExists() - Wrapper for ldap_modify()
126 * works like gfLdapModify, but returns true if the LDAP entry does not exist
129 * @param string entry
132 function gfLdapModifyIfExists($dn,$entry) {
133 $res = $this->gfLdapModify($dn,$entry);
137 $err = ldap_errno ($ldap_conn) ;
147 * gfLdapModAdd() - Wrapper for ldap_mod_add()
150 * @param string entry
153 function gfLdapModAdd($dn,$entry) {
155 return @ldap_mod_add($ldap_conn,$dn,$entry);
159 * gfLdapModDel() - Wrapper for ldap_mod_del()
162 * @param string entry
165 function gfLdapModDel($dn,$entry) {
167 return @ldap_mod_del($ldap_conn,$dn,$entry);
171 * gfLdapRead() - Wrapper for ldap_read()
174 * @param string filter
178 function gfLdapRead($dn,$filter,$attrs=0) {
180 return @ldap_read($ldap_conn,$dn,$filter,$attrs);
184 * gfLdapError() - Wrapper for ldap_error()
189 function gfLdapError() {
191 return ldap_error($ldap_conn);
195 * gfLdapErrno() - Wrapper for ldap_errno()
200 function gfLdapErrno() {
202 return ldap_errno($ldap_conn);
206 * gfLdapAlreadyExists()
208 function gfLdapAlreadyExists() {
210 return ldap_errno($ldap_conn)==20;
214 * gfLdapDoesNotExist()
216 function gfLdapDoesNotExist() {
218 return ldap_errno($ldap_conn)==16;
222 * User management functions
226 * sysCheckUser() - Check for the existence of a user
228 * @param int The user ID of the user to check
229 * @returns true on success/false on error
232 function sysCheckUser($user_id) {
233 $user =& user_get_object($user_id);
237 return $this->gfLdapcheck_user_by_name($user->getUnixName());
241 * gfLdapcheck_user_by_name() - Check for a user by the username
243 * @param string The username
244 * @returns true on success/false on error
247 function gfLdapcheck_user_by_name($user_name) {
251 if (!$this->gfLdapConnect()) {
255 $dn = 'uid='.$user_name.',ou=People,'.forge_get_config('ldap_base_dn');
256 $res = $this->gfLdapRead($dn,"objectClass=*",array("uid"));
258 ldap_free_result($res);
266 * sysCreateUser() - Create a user
268 * @param int The user ID of the user to create
269 * @returns The return status of gfLdapcreate_user_from_object()
272 function sysCreateUser($user_id) {
273 // Check even if the user shouldn't exist
274 // It can be created by a cron
275 if (!$this->sysCheckUser($user_id)){
276 $user = &user_get_object($user_id);
277 return $this->gfLdapcreate_user_from_object($user);
283 * sysCheckCreateUser() - Check that a user has been created
285 * @param int The ID of the user to check
286 * @returns true on success/false on error
289 function sysCheckCreateUser($user_id) {
290 if (!$this->sysCheckUser($user_id)){
291 $user = &user_get_object($user_id);
292 return $this->gfLdapcreate_user_from_object($user);
298 * gfLdapcreate_user_from_object() - Create a user from information contained within an object
300 * @param object The user object
301 * @returns true on success/false on error
304 function gfLdapcreate_user_from_object(&$user) {
306 if (!$this->gfLdapConnect()) {
309 $dn = 'uid='.$user->getUnixName().',ou=People,'.forge_get_config('ldap_base_dn');
310 $entry['objectClass'][0]='top';
311 $entry['objectClass'][1]='account';
312 $entry['objectClass'][2]='posixAccount';
313 $entry['objectClass'][3]='shadowAccount';
314 $entry['objectClass'][4]='debGforgeAccount';
315 $entry['uid']=$user->getUnixName();
316 $entry['cn']=$this->asciize($user->getRealName());
317 $entry['gecos']=$this->asciize($user->getRealName());
318 $entry['userPassword']='{crypt}'.$user->getUnixPasswd();
319 $entry['homeDirectory'] = account_user_homedir($user->getUnixName());
320 $entry['loginShell']=$user->getShell();
321 $entry['debGforgeCvsShell']="/bin/cvssh"; // unless explicitly set otherwise, developer has write access
322 $entry['debGforgeForwardEmail']=$user->getEmail();
323 $entry['uidNumber']=$this->getUnixUID();
324 $entry['gidNumber']=$this->getUnixGID(); // users as in debian backend
325 $entry['shadowLastChange']=1; // We don't have expiration, so any non-0
326 $entry['shadowMax']=99999;
327 $entry['shadowWarning']=7;
329 if (!$this->gfLdapAdd($dn,$entry)) {
330 $this->setError("Error: cannot add LDAP user entry '".
331 $user->getUnixName()."': ".$this->gfLdapError()."<br />");
338 * gfLdapCreateUserFromProps() - Creates an LDAP user from
340 * @param string The username
342 * @param string The encrypted password
343 * @returns true on success/false on error
346 function gfLdapCreateUserFromProps($username, $cn, $crypt_pw,
347 $shell, $cvsshell, $uid, $gid, $email) {
349 if (!$this->gfLdapConnect()) {
352 $dn = 'uid='.$username.',ou=People,'.forge_get_config('ldap_base_dn');
353 $entry['objectClass'][0]='top';
354 $entry['objectClass'][1]='account';
355 $entry['objectClass'][2]='posixAccount';
356 $entry['objectClass'][3]='shadowAccount';
357 $entry['objectClass'][4]='debGforgeAccount';
358 $entry['uid']=$username;
359 $entry['cn']=$this->asciize($cn);
360 $entry['gecos']=$this->asciize($cn);
361 $entry['userPassword']='{crypt}'.$crypt_pw;
362 $entry['homeDirectory'] = account_user_homedir($username);
363 $entry['loginShell']=$shell;
364 $entry['debGforgeCvsShell']=$cvsshell;
365 $entry['debGforgeForwardEmail']=$email;
366 $entry['uidNumber']=$uid;
367 $entry['gidNumber']=$gid;
368 $entry['shadowLastChange']=1;
369 $entry['shadowMax']=99999;
370 $entry['shadowWarning']=7;
372 if (!$this->gfLdapAdd($dn,$entry)) {
373 $this->setError("Error: cannot add LDAP user entry '".
374 $username."': ".$this->gfLdapError()."<br />");
381 * sysRemoveUser() - Remove an LDAP user
383 * @param int The user ID of the user to remove
384 * @returns true on success/false on failure
387 function sysRemoveUser($user_id) {
390 $user = &user_get_object($user_id);
391 if (!$this->gfLdapConnect()) {
394 $dn = 'uid='.$user->getUnixName().',ou=People,'.forge_get_config('ldap_base_dn');
396 if (!$this->gfLdapDelete($dn)) {
397 $this->setError("Error: cannot delete LDAP user entry '".
398 $user->getUnixName()."': ".$this->gfLdapError()."<br />");
405 * sysUserSetAttribute() - Set an attribute for a user
407 * @param int The user ID
408 * @param string The attribute to set
409 * @param string The new value of the attribute
410 * @returns true on success/false on error
413 function sysUserSetAttribute($user_id,$attr,$value) {
416 $user = &user_get_object($user_id);
417 if (!$this->gfLdapConnect()) {
420 $dn = 'uid='.$user->getUnixName().',ou=People,'.forge_get_config('ldap_base_dn');
421 $entry[$attr]=$value;
423 if (!$this->gfLdapModifyIfExists($dn, $entry)) {
424 $this->setError("Error: cannot change LDAP attribute '$attr' for user '".
425 $user->getUnixName()."': ".$this->gfLdapError()."<br />");
432 * Group management functions
436 * sysCheckGroup() - Check for the existence of a group
438 * @param int The ID of the group to check
439 * @returns true on success/false on error
442 function sysCheckGroup($group_id) {
446 $group = &group_get_object($group_id);
448 $this->setError("Error: Cannot find group [$group_id]<br />");
451 if (!$this->gfLdapConnect()) {
454 $dn = 'cn='.$group->getUnixName().',ou=Group,'.forge_get_config('ldap_base_dn');
455 $res=$this->gfLdapRead($dn, "objectClass=*", array("cn"));
457 ldap_free_result($res);
464 * sysCreateGroup() - Create a group
466 * @param int The ID of the group to create
467 * @returns true on success/false on error
470 function sysCreateGroup($group_id) {
473 $group = &group_get_object($group_id);
474 if (!$this->gfLdapConnect()) {
477 $dn = 'cn='.$group->getUnixName().',ou=Group,'.forge_get_config('ldap_base_dn');
478 $entry['objectClass'][0]='top';
479 $entry['objectClass'][1]='posixGroup';
480 $entry['cn']=$group->getUnixName();
481 $entry['userPassword']='{crypt}x';
482 $entry['gidNumber']=$this->getUnixGID();
488 if (!$this->gfLdapAdd($dn,$entry)) {
489 $this->setError("Error: cannot add LDAP group entry '".
490 $group->getUnixName()."': ".$this->gfLdapError()."<br />");
491 // If there's error, that's bad. But don't stop.
496 // Now create CVS group
499 // Add virtual anoncvs user to CVS group
500 $cvs_member_list[$i_cvs++] = 'anoncvs_'.$group->getUnixName();
502 $dn = 'cn='.$group->getUnixName().',ou=cvsGroup,'.forge_get_config('ldap_base_dn');
504 if ($cvs_member_list) {
505 $entry['memberUid']=$cvs_member_list;
507 unset($entry['memberUid']);
510 if (!$this->gfLdapAdd($dn,$entry)) {
511 $this->setError("Error: cannot add LDAP CVS group entry '"
512 .$group->getUnixName()."': ".$this->gfLdapError()."<br />");
517 // Finally, setup AnonCVS virtual user
520 if (!$this->gfLdapcheck_user_by_name('anoncvs_'.$group->getUnixName())
521 && !$this->gfLdapCreateUserFromProps('scm_'.$group->getUnixName(),
523 '/bin/false', '/bin/false',
525 $this->getUnixGID(), "/dev/null")) {
526 $this->setError("Error: cannot add LDAP AnonCVS user entry '"
527 .$group->getUnixName()."': ".$this->gfLdapError()."<br />");
535 * sysRemoveGroup() - Remove a group
537 * @param int The ID of the group to remove
538 * @returns true on success/false on error
541 function sysRemoveGroup($group_id) {
544 $group = &group_get_object($group_id);
545 if (!$this->gfLdapConnect()) {
550 // Remove shell LDAP group
554 $dn = 'cn='.$group->getUnixName().',ou=Group,'.forge_get_config('ldap_base_dn');
556 if (!$this->gfLdapDelete($dn)) {
557 $this->setError("Error: cannot delete LDAP group entry '".
558 $group->getUnixName()."': ".$this->gfLdapError()."<br />");
563 // Remove CVS LDAP group
566 $dn = 'cn='.$group->getUnixName().',ou=cvsGroup,'.forge_get_config('ldap_base_dn');
568 if (!$this->gfLdapDelete($dn)) {
569 $this->setError("Error: cannot delete LDAP CVS group entry '".
570 $group->getUnixName()."': ".$this->gfLdapError()."<br />");
575 // Remove AnonCVS virtual user
578 $dn = 'uid=anoncvs_'.$group->getUnixName().',ou=People,'.forge_get_config('ldap_base_dn');
579 if (!$this->gfLdapDelete($dn)) {
580 $this->setError("Error: cannot delete LDAP AnonCVS user entry '".
581 $group->getUnixName()."': ".$this->gfLdapError()."<br />");
588 function sysGroupCheckUser($group_id,$user_id) {
590 if (! $this->sysGroupRemoveUser($group_id,$user_id)) {
595 $u = user_get_object($user_id) ;
596 $p = group_get_object($group_id) ;
597 if (forge_check_perm_for_user($u,'scm',$group_id,'write')) {
598 if ($u->isMember($p)) {
599 $this->sysGroupAddUser($group_id,$user_id,false) ;
601 $this->sysGroupRemoveUser($group_id,$user_id,false) ;
602 $this->sysGroupAddUser($group_id,$user_id,true) ;
605 if ($u->isMember($p)) {
606 $this->sysGroupAddUser($group_id,$user_id,false) ;
607 $this->sysGroupRemoveUser($group_id,$user_id,true) ;
609 $this->sysGroupRemoveUser($group_id,$user_id,false) ;
615 * sysGroupAddUser() - Add a user to an LDAP group
617 * @param int The ID of the group two which the user will be added
618 * @param int The ID of the user to add
619 * @param bool Only add this user to CVS
620 * @returns true on success/false on error
623 function sysGroupAddUser($group_id,$user_id,$cvs_only=0) {
627 $group = &group_get_object($group_id);
628 $user = &user_get_object($user_id);
629 if (!$this->gfLdapConnect()) {
632 $dn = 'cn='.$group->getUnixName().',ou=Group,'.forge_get_config('ldap_base_dn');
633 $cvs_dn = 'cn='.$group->getUnixName().',ou=cvsGroup,'.forge_get_config('ldap_base_dn');
634 $entry['memberUid'] = $user->getUnixName();
637 // Check if user already a member of CVS group
640 $res=$this->gfLdapRead($cvs_dn,"memberUid=".$user->getUnixName(),array("cn"));
641 if ($res && ldap_count_entries($ldap_conn,$res)>0) {
642 //echo "already a member of CVS<br />";
648 if (!$this->gfLdapModAdd($cvs_dn,$entry)) {
649 $this->setError("Error: cannot add member to LDAP CVS group entry '".
650 $group->getUnixName()."': ".$this->gfLdapError()."<br />");
655 ldap_free_result($res);
662 // Check if user already a member of shell group
664 $res = $this->gfLdapRead($dn, "memberUid=".$user->getUnixName(), array("cn"));
666 if ($res && ldap_count_entries($ldap_conn,$res)>0) {
667 //echo "already a member<br />";
673 if (!$this->gfLdapModAdd($dn,$entry)) {
674 $this->setError("Error: cannot add member to LDAP group entry '".
675 $group->getUnixName()."': ".$this->gfLdapError()."<br />");
680 ldap_free_result($res);
686 * sysGroupRemoveUser() - Remove a user from an LDAP group
688 * @param int The ID of the group from which to remove the user
689 * @param int The ID of the user to remove
690 * @param bool Only remove user from CVS group
691 * @returns true on success/false on error
694 function sysGroupRemoveUser($group_id,$user_id,$cvs_only=0) {
697 $group = &group_get_object($group_id);
698 $user = &user_get_object($user_id);
699 if (!$this->gfLdapConnect()) {
703 $dn = 'cn='.$group->getUnixName().',ou=Group,'.forge_get_config('ldap_base_dn');
704 $cvs_dn = 'cn='.$group->getUnixName().',ou=cvsGroup,'.forge_get_config('ldap_base_dn');
705 $entry['memberUid'] = $user->getUnixName();
709 if (!$this->gfLdapModDel($cvs_dn,$entry) && !$this->gfLdapDoesNotExist()) {
710 $this->setError("Error: cannot remove member from LDAP CVS group entry '".
711 $group->getUnixName()."': ".$this->gfLdapError()."(".$this->gfLdapErrno().")"."<br />");
719 if (!$this->gfLdapModDel($dn,$entry) && !$this->gfLdapDoesNotExist()) {
720 $this->setError("Error: cannot remove member from LDAP group entry '".
721 $group->getUnixName()."': ".$this->gfLdapError()."(".$this->gfLdapErrno().")"."<br />");
731 // c-file-style: "bsd"