1 <?php
2 /*
3 * This code is part of GOsa (http://www.gosa-project.org)
4 * Copyright (C) 2003-2008 GONICUS GmbH
5 *
6 * ID: $$Id: class_sudo.inc 9975 2008-03-25 14:09:30Z hickert $$
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
24 /*! \brief Sudo generic class. Allow setting User/Host/Command/Runas
25 for a sudo role object.
26 */
27 class sudo extends plugin
28 {
30 protected $cn= "";
31 protected $description= "";
33 protected $sudoUser = array("ALL");
34 protected $sudoCommand= array();
35 protected $sudoHost = array("ALL");
36 protected $sudoRunAs = array("ALL");
38 private $is_default = FALSE;
40 public $objectclasses = array("top","sudoRole");
41 public $attributes = array("cn","description","sudoUser","sudoCommand","sudoHost","sudoRunAs");
43 public $ignore_account = TRUE;
46 public $orig_dn;
47 private $systemEnabled = FALSE;
49 /*! \brief Returns to the base department for sudo roles.
50 This department is then used to store new roles.
51 @param Object GOsa configuration object.
52 @return String sudo store department
53 */
54 public static function get_sudoers_ou($config)
55 {
56 return(get_ou("sudo", "sudoRDN").$config->current['BASE']);
57 }
59 /*! \brief Initializes this sudo class, with all required attributes.
60 @param Object $config GOsa configuration object.
61 @param String $db "new" or the sudo role dn.
62 @return .
63 */
64 function sudo(&$config, $dn= NULL)
65 {
66 plugin::plugin ($config, $dn);
68 $this->systemEnabled = class_available("systemSelect");
70 $this->trustModeDialog = new trustModeDialog($this->config, $this->dn,NULL);
71 $this->trustModeDialog->setAcl('sudo/sudo');
73 if($this->initially_was_account){
74 foreach(array("sudoUser","sudoCommand","sudoHost","sudoRunAs") as $attr){
75 $this->$attr = array();
76 if(isset($this->attrs[$attr])){
77 $tmp = array();
78 for($i = 0 ; $i < $this->attrs[$attr]['count']; $i++){
79 $tmp[] = $this->attrs[$attr][$i];
80 }
81 $this->$attr = $tmp;
82 }
83 }
84 }
86 if(preg_match("/^defaults$/i",$this->cn)){
87 $this->is_default = TRUE;
88 }
90 /* Get global filter config */
91 if (!session::is_set("sysfilter")){
92 $ui= get_userinfo();
93 $base= get_base_from_people($ui->dn);
94 $sysfilter= array( "depselect" => $base,
95 "regex" => "*");
96 session::set("sysfilter", $sysfilter);
97 }
99 $this->orig_dn = $this->dn;
101 // Build sortable lists
102 foreach(array('sudoUser','sudoCommand','sudoHost','sudoRunAs') as $l){
103 $ll = $l."List";
104 $this->$ll = new sortableListing($this->$l);
105 $this->$ll->setDeleteable(false);
106 $this->$ll->setEditable(false);
107 $this->$ll->setWidth("100%");
108 $this->$ll->setHeight("100px");
109 $this->$ll->setAcl($this->getacl($l));
110 $this->$ll->setDefaultSortColumn(1);
111 }
112 $this->sudoUserList->setHeader(array("!",_("Type"),_("Member"),_("Option")));
113 $this->sudoUserList->setColspecs(array('24px','24px','*','46px'));
114 $this->sudoUserList->setDefaultSortColumn(2);
115 $this->sudoHostList->setHeader(array("!",_("System"),_("Option")));
116 $this->sudoHostList->setColspecs(array('24px','*','46px'));
117 $this->sudoCommandList->setHeader(array("!",_("Command"),_("Option")));
118 $this->sudoCommandList->setColspecs(array('24px','*','46px'));
119 $this->sudoRunAsList->setHeader(array("!",_("User"),_("Option")));
120 $this->sudoRunAsList->setColspecs(array('24px','*','46px'));
121 }
124 /*! \brief Creates the sudo generic ui.
125 @return String The generated HTML content for this plugin.
126 */
127 function execute()
128 {
129 /* Call parent execute */
130 plugin::execute();
132 // Set list ACLs
133 foreach(array('sudoUser','sudoCommand','sudoHost','sudoRunAs') as $l){
134 $ll = $l."List";
135 $this->$ll->setAcl($this->getacl($l));
136 }
138 // Handle trust mode dialog
139 $trustModeDialog = $this->trustModeDialog->execute();
140 if($this->trustModeDialog->trustSelect){
141 $this->dialog = TRUE;
142 return($trustModeDialog);
144 }
146 if(!is_object($this->dialog)){
147 $this->dialog = FALSE;
148 }
150 /*********************
151 Add users
152 *********************/
154 if(isset($_POST['list_sudoUser']) && !is_object($this->dialog) && $this->acl_is_writeable("sudoUser")){
155 $this->dialog =new userGroupSelect($this->config,get_userinfo());
156 }
158 /* Add selected hosts to the sudoUser list */
159 if(isset($_POST['userGroupSelect_save']) && $this->dialog instanceof userGroupSelect){
160 if($this->acl_is_writeable("sudoUser")){
161 foreach($this->dialog->save() as $entry){
162 if(in_array_strict("posixGroup",$entry['objectClass'])){
163 $name = trim("%".$entry['cn'][0]);
164 }elseif(isset($entry['uid'][0])){
165 $name = trim($entry['uid'][0]);
166 }
167 if(!in_array_strict($name,$this->sudoUser) && !in_array_strict("!".$name,$this->sudoUser)){
168 $this->sudoUser[] = $name;
169 }
170 }
171 }
172 unset($this->dialog);
173 $this->dialog = NULL;
174 }
176 if(isset($_POST['userGroupSelect_cancel']) && $this->dialog instanceOf userGroupSelect){
177 unset($this->dialog);
178 $this->dialog = NULL;
179 }
181 if($this->dialog instanceOf userGroupSelect){
182 $used = array();
183 foreach($this->sudoUser as $name){
184 $str = preg_replace("/^!/","",$name);
185 if(preg_match("/^%/", $str)){
186 $used['cn'][] = preg_replace("/^%/","",$str);
187 }else{
188 $used['uid'][] = $str;
189 }
190 }
192 // Build up blocklist
193 session::set('filterBlacklist', $used);
194 return($this->dialog->execute());
195 }
199 /*********************
200 Add systems
201 *********************/
203 if(isset($_POST['list_sudoHost']) && !is_object($this->dialog) && $this->acl_is_writeable("sudoHost")){
204 $this->dialog =new systemSelect($this->config,get_userinfo());
205 }
207 /* Add selected hosts to the sudoHost list */
208 if(isset($_POST['systemSelect_save']) && $this->dialog instanceof systemSelect){
209 if($this->acl_is_writeable("sudoHost")){
210 foreach($this->dialog->save() as $entry){
211 $cn = trim($entry['cn'][0]);
212 if(!in_array_strict($cn,$this->sudoHost) && !in_array_strict("!".$cn,$this->sudoHost)){
213 $this->sudoHost[] = $cn;
214 }
215 }
216 }
217 unset($this->dialog);
218 $this->dialog = NULL;
219 }
221 if(isset($_POST['systemSelect_cancel']) && $this->dialog instanceOf systemSelect){
222 unset($this->dialog);
223 $this->dialog = NULL;
224 }
226 if($this->dialog instanceOf systemSelect){
227 $used = array();
228 foreach($this->sudoHost as $name){
229 $used['cn'][] = preg_replace("/^!/","",$name);
230 }
232 // Build up blocklist
233 session::set('filterBlacklist', $used);
234 return($this->dialog->execute());
235 }
237 /*********************
238 Dialog handling / display / close
239 *********************/
241 if(is_object($this->dialog)){
242 return($this->dialog->execute());
243 }
246 /*********************
247 NEGATE values
248 *********************/
249 foreach($_POST as $name => $value){
250 $value =get_post($name);
251 if(preg_match("/^neg_/",$name)){
252 $attr = preg_replace("/^neg_([^_]*)_.*$/","\\1",$name);
253 $value= preg_replace("/^neg_[^_]*_([0-9]*)$/","\\1",$name);
255 if($this->acl_is_writeable($attr)){
256 $attrs = $this->$attr;
257 if(isset( $attrs[$value])){
258 $v = $attrs[$value];
259 if(preg_match("/^!/",$v)){
260 $attrs[$value] = preg_replace("/^!/","",$v);
261 }else{
262 $attrs[$value] = "!".$v;
263 }
264 $this->$attr = $attrs;
265 }
266 }
267 break; // Do it once, image inputs will be posted twice
268 }
269 }
271 /*********************
272 Delete values
273 *********************/
274 foreach($_POST as $name => $value){
275 $value =get_post($name);
276 if(preg_match("/^delS_/",$name)){
277 $attr = preg_replace("/^delS_([^_]*).*$/","\\1",$name);
278 $value= preg_replace("/^delS_[^_]*_([0-9]*)$/","\\1",$name);
279 if($this->acl_is_writeable($attr)){
280 $attrs = $this->$attr;
281 if(isset( $attrs[$value])){
282 unset($attrs[$value]);
283 $this->$attr = $attrs;
284 }
285 }
286 break; // Do it once, image inputs will be posted twice
287 }
288 }
291 /*********************
292 ADD values
293 *********************/
295 /* User / Host / Runas */
296 foreach(array("sudoUser","sudoHost","sudoRunAs") as $attr){
297 if($this->acl_is_writeable($attr) &&
298 isset($_POST["add_".$attr]) &&
299 isset($_POST['new_'.$attr]) &&
300 !empty($_POST['new_'.$attr])){
302 $c = preg_quote(' *+-?_|!\'"()','/');
303 if(preg_match("/^[a-z0-9{$c}]*$/i",get_post('new_'.$attr))){
304 $attrs = $this->$attr;
305 $attrs[] = trim(get_post('new_'.$attr));
306 $this->$attr = $attrs;
307 }else{
308 msg_dialog::display(_("Error"),msgPool::invalid($attr,get_post('new_'.$attr),"/[a-z0-9{$c}]/i"));
309 }
310 }
311 }
313 /* Command */
314 foreach(array("sudoCommand") as $attr){
315 if($this->acl_is_writeable($attr) && isset($_POST["add_".$attr]) && isset($_POST['new_'.$attr])){
316 $attrs = $this->$attr;
317 $attrs[] = trim(get_post('new_'.$attr));
318 $this->$attr = $attrs;
319 }
320 }
323 /*********************
324 SMARTY assignments
325 *********************/
327 $smarty = get_smarty();
328 $smarty->assign("systemEnabled", $this->systemEnabled);
329 $smarty->assign("trustModeDialog" , $trustModeDialog);
330 $smarty->assign("is_default",$this->is_default);
331 foreach($this->attributes as $attr){
332 $smarty->assign($attr, set_post($this->$attr));
333 $smarty->assign($attr."ACL",$this->getacl($attr));
334 }
336 /* Fill listings
337 */
338 $neg_img= image('plugins/sudo/images/negate.png','','!');
339 $option = image('plugins/sudo/images/negate.png','neg_%ATTR%_%KEY%');
340 $option.= image('images/lists/trash.png', 'delS_%ATTR%_%KEY%');
341 foreach(array('sudoUser','sudoCommand','sudoHost','sudoRunAs') as $l){
342 $l.="Data";
343 $$l = array();
344 }
346 foreach(array("sudoCommand","sudoHost","sudoRunAs") as $attr){
347 $tmp =array();
348 $list = $attr."List";
349 $data = $attr."Data";
350 foreach($this->$attr as $id => $entry){
351 $neg = "";
352 if(preg_match("/^!/",$entry)){
353 $neg = $neg_img;
354 }
355 $entry = preg_replace("/^!/","",$entry);
357 $action =preg_replace(array("/%KEY%/","/%ATTR%/"),array($id,$attr),$option);
358 $tmp[$id] = array('data'=>array($neg,$entry,$action)) ;
359 }
360 $this->$list->setListData($this->$attr, $tmp);
361 $this->$list->update();
362 $smarty->assign("listing_{$attr}", $this->$list->render());
363 }
366 $img1 = image('plugins/users/images/select_user.png','',_("User"));
367 $img2 = image('plugins/groups/images/select_group.png','',_("Group"));
368 $sudoUserData = array();
369 foreach($this->sudoUser as $id => $entry){
370 $neg = "";
371 if(preg_match("/^!/",$entry)){
372 $neg = $neg_img;
373 }
374 $entry = preg_replace("/^!/","",$entry);
376 $img = $img1;
377 if(preg_match("/^%/",$entry)){
378 $img = $img2;
379 }
380 $entry = preg_replace("/^%/","",$entry);
381 $action =preg_replace(array("/%KEY%/","/%ATTR%/"),array($id,'sudoUser'),$option);
382 $sudoUserData[$id] = array('data'=>array($neg,$img,$entry,$action)) ;
383 }
384 $this->sudoUserList->setListData($this->sudoUser,$sudoUserData);
385 $this->sudoUserList->update();
386 $smarty->assign("listing_sudoUser", $this->sudoUserList->render());
387 return($smarty->fetch(get_template_path('generic.tpl', TRUE)));
388 }
391 /*! \brief Remove this sudo role from the ldap server
392 */
393 function remove_from_parent()
394 {
395 plugin::remove_from_parent();
397 $ldap = $this->config->get_ldap_link();
398 $ldap->cd($this->dn);
399 $ldap->rmdir($this->dn);
401 /* Send signal to the world that we've done */
402 $this->handle_post_events("remove");
403 }
406 /*! \brief Save all relevant HTML posts.
407 */
408 function save_object()
409 {
410 plugin::save_object();
411 $this->trustModeDialog->save_object();
413 if($this->is_default){
414 $this->cn = "defaults";
415 }
416 }
419 function set_acl_base($base)
420 {
421 plugin::set_acl_base($base);
422 $this->trustModeDialog->set_acl_base($base);
423 }
426 /*! \brief Save changes into the ldap database.
427 */
428 function save()
429 {
430 plugin::save();
432 /* Ensure a correct array index
433 */
434 $this->attrs['sudoHost'] = array_values($this->attrs['sudoHost']);
435 $this->attrs['sudoRunAs'] = array_values($this->attrs['sudoRunAs']);
436 $this->attrs['sudoUser'] = array_values($this->attrs['sudoUser']);
437 $this->attrs['sudoCommand'] = array_values($this->attrs['sudoCommand']);
439 $this->cleanup();
441 $ldap = $this->config->get_ldap_link();
442 $ldap->cd($this->config->current['BASE']);
444 if($this->is_new){
445 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $this->dn));
446 $ldap->cd($this->dn);
447 $ldap->add($this->attrs);
449 /* Send signal to the world that we've done */
450 $this->handle_post_events("add");
451 }else{
452 $ldap->cd($this->dn);
453 $ldap->modify($this->attrs);;
455 /* Send signal to the world that we've done */
456 $this->handle_post_events("modify");
457 }
458 $this->trustModeDialog->dn = $this->dn;
459 $this->trustModeDialog->save();
461 if (!$ldap->success()){
462 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_DEL, get_class()));
463 }
464 }
467 /*! \brief Check the given input.
468 @return Array All error messages in an array();
469 */
470 function check()
471 {
472 $message = plugin::check();
474 /* Is a name given? */
475 if($this->cn == ""){
476 $message[] = msgPool::required(_("Name"));
477 }
479 /* Check if name is reserved */
480 if(!$this->is_default && preg_match("/^defaults$/i",$this->cn)){
481 $message[] = msgPool::reserved(_("Name"));
482 }
484 /* Check name */
485 if(!preg_match("/^[0-9a-z\@]*$/i",$this->cn)){
486 $message[] = msgPool::invalid(_("Name"),$this->cn,"/[0-9a-z\@]/i");
487 }
489 /* Check if this entry will cause duplicated ldap entries */
490 $ldap = $this->config->get_ldap_link();
491 $ldap->cd($this->get_sudoers_ou($this->config));
492 $ldap->search("(&(objectClass=sudoRole)(cn=".$this->cn."))");
493 while($attrs = $ldap->fetch()){
494 if($attrs['dn'] != $this->dn){
495 $message[] = msgPool::duplicated(_("Name"));
496 }
497 }
499 /* Check if we are allowed to create or move this object
500 */
501 if($this->orig_dn == "new" && !$this->acl_is_createable($this->get_sudoers_ou($this->config))){
502 $message[] = msgPool::permCreate();
503 }
505 return ($message);
506 }
509 /*! \brief Force this entry to be handled and saved as 'default'
510 @param BOOL TRUE -force defaults FALSE -normal
511 */
512 public function set_default($state)
513 {
514 $this->is_default = TRUE;
515 $this->cn = "defaults";
516 }
519 /*! \brief Add ACL object
520 @return Returns the ACL object.
521 */
522 static function plInfo()
523 {
524 return (array(
525 "plShortName" => _("Sudo"),
526 "plDescription" => _("Sudo role"),
527 "plSelfModify" => FALSE,
528 "plDepends" => array(),
529 "plPriority" => 0,
530 "plSection" => array("administration"),
531 "plCategory" => array("sudo" => array("objectClass" => "sudoRole", "description" => _("Sudo role"))),
533 "plProperties" =>
534 array(
535 array(
536 "name" => "sudoRDN",
537 "type" => "rdn",
538 "default" => "ou=sudoers,",
539 "description" => _("RDN for sudo rule storage."),
540 "check" => "gosaProperty::isRdn",
541 "migrate" => "migrate_sudoRDN",
542 "group" => "plugin",
543 "mandatory" => FALSE)),
546 "plProvidedAcls" => array(
547 "accessTo" => _("System trust"),
548 "cn" => _("Name"),
549 "description" => _("Description"),
550 "sudoUser" => _("Users"),
551 "sudoHost" => _("Host"),
552 "sudoCommand" => _("Command"),
553 "sudoRunAs" => _("Run as user"),
554 "trustModel" => _("Access control list"))
555 ));
556 }
559 /*! \brief This function will be called if an object gets copied.
560 This function adapts attributes from the source object.
561 @param Array The source object.
562 */
563 function PrepareForCopyPaste($source)
564 {
565 plugin::PrepareForCopyPaste($source);
567 $this->trustModeDialog->PrepareForCopyPaste($source);
569 foreach(array("sudoUser","sudoCommand","sudoHost","sudoRunAs") as $attr){
570 $this->$attr = array();
571 if(isset($source[$attr])){
572 $tmp = array();
573 for($i = 0 ; $i < $source[$attr]['count']; $i++){
574 $tmp[] = $source[$attr][$i];
575 }
576 $this->$attr = $tmp;
577 }
578 }
579 }
582 /*! \brief Used for copy & paste.
583 Returns a HTML input mask, which allows to change the cn of this entry.
584 @param Array Array containing current status && a HTML template.
585 */
586 function getCopyDialog()
587 {
588 $vars = array("cn");
589 $smarty = get_smarty();
590 $smarty->assign("cn", set_post($this->cn));
591 $str = $smarty->fetch(get_template_path("paste_generic.tpl",TRUE));
592 $ret = array();
593 $ret['string'] = $str;
594 $ret['status'] = "";
595 return($ret);
596 }
599 public function get_cn()
600 {
601 return($this->cn);
602 }
605 /*! \brief Used for copy & paste.
606 Some entries must be renamed to avaoid duplicate entries.
607 */
608 function saveCopyDialog()
609 {
610 if(isset($_POST['cn'])){
611 $this->cn = get_post('cn');
612 }
613 }
614 }
615 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
616 ?>