1 <?php
3 /*
4 * This code is part of GOsa (http://www.gosa-project.org)
5 * Copyright (C) 2003-2008 GONICUS GmbH
6 * Copyright (C) 2010 Thomas CHEMINEAU
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 */
23 /**
24 * This class will allow user to parameter dynamic group.
25 * @author Thomas Chemineau - thomas.chemineau<at>gmail.com
26 * @version 0.01
27 */
28 class DynamicLdapGroup extends plugin
29 {
31 /**
32 * The attribute that will use GOsa to store LDAP URI.
33 * @var array
34 */
35 public $attributes = array('labeledURI');
37 /**
38 * The objectClass that will use GOsa to identify a group as dynamic.
39 * @var array
40 */
41 public $objectclasses = array('labeledURIObject');
43 /**
44 * Default value for the corresponding attribute found in the $this->attributes
45 * array of this plugin.
46 * @var string
47 */
48 public $labeledURI = array();
49 public $labeledURIparsed = array();
50 public $labeledURIdefault = 'ldap:///dc=example,dc=com?memberUid?sub?(objectClass=posixGroup)';
52 public $scopes = array('base','one','sub');
54 /**
55 * Store values of memberUrl.
56 * @var Array
57 */
58 private $_memberUrls = Array();
60 public $orig_dn ="";
62 /**
63 * Create this object.
64 * @param Array $config GOsa config.
65 * @param string $dn Current DN.
66 */
67 public function __construct ($config, $dn)
68 {
69 parent::__construct($config, $dn);
71 // Load labeledURI values.
72 $this->labeledURI = array();
73 if(!$this->is_account){
74 $this->labeledURI[] = str_replace('dc=example,dc=com', LDAP::fix($this->dn), $this->labeledURIdefault);
75 }elseif(isset($this->attrs['labeledURI'])){
76 for($i =0; $i < $this->attrs['labeledURI']['count']; $i++) {
77 $this->labeledURI[] = $this->attrs['labeledURI'][$i];
78 }
79 }
81 // Parse labeledURI entries
82 $this->labeledURIparsed = array();
83 foreach($this->labeledURI as $entry){
84 list($base,$attr,$scope,$filter) = preg_split("/\?/",$entry);
86 // Ignore entries that do not have a valid scope value (one,base,sub)
87 if(!in_array_strict($scope,array('base','one','sub'))) continue;
89 // Append parsed uri
90 $scope = array_search($scope,$this->scopes);
91 $this->labeledURIparsed[] = array('base' => $base, 'attr' => $attr, 'scope' => $scope,'filter' => $filter);
92 }
94 // Save dn, to be able the check for object movements - put this in plugin::move
95 $this->orig_dn = $this->dn;
96 }
99 public function check ()
100 {
101 $messages = plugin::check();
103 // At least one entry is required.
104 if(!count($this->labeledURIparsed)){
105 $messages[] = msgPool::required(_("Labeled URI"));
106 }
108 // Check entries
109 foreach($this->labeledURIparsed as $key => $entry){
110 $nr = $key +1;
112 // A base is required
113 if(empty($entry['base'])){
114 $messages[] = msgPool::required(_("Base")." {$nr}");
115 }
117 // Check for invalid attributes
118 if(empty($entry['attr'])){
119 $messages[] = msgPool::required(_("Attribute")." {$nr}");
120 }elseif(in_array_strict(strtolower($entry['attr']), array('objectclass'))){
121 $messages[] = msgPool::reserved(_("Attribute")." {$nr}");
122 }
124 // A filter is required
125 if(empty($entry['filter'])){
126 $messages[] = msgPool::required(_("Filter")." {$nr}");
127 }elseif(!preg_match("/^\(/", $entry['filter'])){
128 $messages[] = msgPool::invalid(_("Filter")." {$nr}", $entry['filter'],'', '(objectClass=gosaAccount)'." - "._("Surrounding brackets are required!"));
129 }else{
131 // Check if filter is valid
132 $ldap = $this->config->get_ldap_link();
133 $ldap->cd($this->config->current['BASE']);
134 $ldap->search($entry['filter']);
135 if(!$ldap->success()){
136 $messages[] = sprintf(_("The given filter '%s' for entry %s seems to be invalid!"),
137 bold($entry['filter']), $nr);
138 }
139 }
140 }
142 return($messages);
143 }
146 /**
147 * Execute this plugin.
148 * @return string HTML to print.
149 */
150 public function execute ()
151 {
152 //
153 // Are we trying to modify state of this group ? If so,
154 // we can edit the current object.
155 //
156 if (isset($_POST['modify_state']))
157 {
158 $this->is_account = !$this->is_account;
159 }
161 //
162 // Display a message if this feature is disabled.
163 //
164 if (!$this->is_account)
165 {
166 return $this->show_disable_header(msgPool::addFeaturesButton(_("Dynamic object")), msgPool::featuresDisabled(_("Dynamic object")));
167 }
168 $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("Dynamic object")), msgPool::featuresEnabled(_("Dynamic object")));
170 // Display values.
171 //
172 $smarty = get_smarty();
173 $smarty->assign('labeledURIparsed', set_post($this->labeledURIparsed));
174 $smarty->assign('scopes', set_post($this->scopes));
175 $display .= $smarty->fetch(get_template_path('dyngroup.tpl', TRUE, dirname(__FILE__)));
176 return $display;
177 }
181 /**
182 * This plugin does nothing when this method is invoked.
183 */
184 public function remove_from_parent ()
185 {
186 parent::remove_from_parent();
187 $ldap = $this->config->get_ldap_link();
188 $ldap->cd($this->dn);
189 $ldap->modify($this->attrs);
190 if(!$ldap->success()){
191 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_DEL, get_class()));
192 }
193 return;
194 }
197 /**
198 * This function is called when tab is undisplayed. For example, the current user
199 * wants to change other settings of this group, but not save it to the LDAP
200 * directory directly.
201 */
202 public function save_object ()
203 {
204 parent::save_object();
206 // Add a new labeled Uri
207 if(isset($_POST['addUri'])){
208 $this->labeledURIparsed[] =
209 array(
210 'base' => 'ldap:///'.$this->dn,
211 'attr' => 'memberUid',
212 'scope' => 2,
213 'filter' => '(objectClass=posixGroup)');
214 }
216 // Remove a labeled Uri and get posts
217 foreach($this->labeledURIparsed as $key => $data){
218 foreach(array('scope','attr','filter','base') as $attr){
219 if(isset($_POST[$attr.'_'.$key])){
220 $this->labeledURIparsed[$key][$attr] = get_post($attr.'_'.$key);
221 }
222 }
224 // Remove labeled uri if requested
225 if(isset($_POST['delUri_'.$key])){
226 unset($this->labeledURIparsed[$key]);
227 }
228 }
229 $this->labeledURIparsed = array_values($this->labeledURIparsed);
230 }
233 /**
234 * That will add additionnal information into the current LDAP entry.
235 * If this plugin is disable, then it will remove any data that references
236 * this plugin into the LDAP directory.
237 * @return boolean
238 */
239 public function save ()
240 {
241 // Build up labeledUri entries
242 $this->labeledURI = array();
243 foreach($this->labeledURIparsed as $entry){
244 $scope = $this->scopes[$entry['scope']];
245 $filter = $entry['filter'];
246 $this->labeledURI[] = "{$entry['base']}?{$entry['attr']}?{$scope}?{$filter}";
247 }
248 $this->labeledURI = array_unique($this->labeledURI);
250 parent::save();
251 $this->cleanup();
252 $ldap = $this->config->get_ldap_link();
253 $ldap->cd($this->dn);
254 $ldap->modify($this->attrs);
255 if(!$ldap->success()){
256 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_DEL, get_class()));
257 }
258 }
261 /*! \brief Updates labeledURI entries in ldap.
262 * Check whether the given src_dn is part of some labeledURI entries
263 * and then updates the entries to use the dst_dn.
264 * @param $config The GOsa configuration object.
265 * @param $src_dn The source 'dn' of the object that was moved.
266 * @param $dst_dn The target 'dn' of the object that was moved.
267 */
268 public static function moveDynGroup($config,$src_dn,$dst_dn)
269 {
270 // Fetch all dynamic group definitions
271 $objs = get_list("(&(objectClass=labeledURIObject)(labeledURI=*))",array("all"),$config->current['BASE'],
272 array("dn","labeledURI"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
273 $newAttrs = array();
274 foreach($objs as $obj){
275 $changes = false;
276 $attrs = array();
277 for($i = 0; $i < $obj['labeledURI']['count']; $i++){
278 $c = $obj['labeledURI'][$i];
279 $c = preg_replace('/'.preg_quote($src_dn,'/').'/',$dst_dn,$c);
280 $attrs['labeledURI'][] = $c;
282 // Check if something has changed
283 if($c != $obj['labeledURI'][$i]){
284 $changes =TRUE;
285 }
286 }
288 // If at least one line of 'labeledURI' has changed then we have to update the whole entry.
289 if($changes) $newAttrs[$obj['dn']] = $attrs;
290 }
292 // If we've at least one entry to update then
293 if(count($newAttrs)){
294 $ldap = $config->get_ldap_link();
295 foreach($newAttrs as $dn => $data){
296 $ldap->cd($dn);
297 $ldap->modify($data);
298 if(!$ldap->success()){
299 trigger_error(sprintf("Failed to dynamic group object for %s: %s", bold($dn), $ldap->get_error()));
300 new log("debug",
301 "plugin/plugin::move()",$dn,array(),
302 " -- ERROR -- Failed to update dynamic groups (labeledURI) - ".$ldap->get_error());
303 }else{
304 new log("modify",
305 "plugin/plugin::move()",$dn,array_keys($data),
306 "Updated dynamic group entries (labeledURI)");
307 }
308 }
309 }
310 }
313 /**
314 * Static method to set ACL for this plugin.
315 */
316 public static function plInfo()
317 {
318 return Array(
319 "plShortName" => _("Dynamic object"),
320 "plDescription" => _("Dynamic object"),
321 "plSelfModify" => TRUE,
322 "plDepends" => Array(),
323 "plPriority" => 1,
324 "plSection" => Array("addon"),
325 "plCategory" => Array("groups", "department", "ogroups"),
326 "plProvidedAcls" => array(
327 'labeledURI' => _('Labeled URI'),
328 )
329 );
330 }
331 }
333 ?>