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 /*!\brief Checks whether the given attribute is managed by this dyngroup extension or not.
100 */
101 function isAttributeDynamic($attr)
102 {
103 if($this->is_account){
104 foreach($this->labeledURIparsed as $uri){
105 if($uri['attr'] == $attr) return(TRUE);
106 }
107 }
108 return(FALSE);
109 }
112 public function check ()
113 {
114 $messages = plugin::check();
116 // At least one entry is required.
117 if(!count($this->labeledURIparsed)){
118 $messages[] = msgPool::required(_("Labeled URI"));
119 }
121 // Check entries
122 foreach($this->labeledURIparsed as $key => $entry){
123 $nr = $key +1;
125 // A base is required
126 if(empty($entry['base'])){
127 $messages[] = msgPool::required(_("Base")." {$nr}");
128 }
130 // Check for invalid attributes
131 if(empty($entry['attr'])){
132 $messages[] = msgPool::required(_("Attribute")." {$nr}");
133 }elseif(in_array_strict(strtolower($entry['attr']), array('objectclass'))){
134 $messages[] = msgPool::reserved(_("Attribute")." {$nr}");
135 }
137 // A filter is required
138 if(empty($entry['filter'])){
139 $messages[] = msgPool::required(_("Filter")." {$nr}");
140 }elseif(!preg_match("/^\(/", $entry['filter'])){
141 $messages[] = msgPool::invalid(_("Filter")." {$nr}", $entry['filter'],'', '(objectClass=gosaAccount)'." - "._("Surrounding brackets are required!"));
142 }else{
144 // Check if filter is valid
145 $ldap = $this->config->get_ldap_link();
146 $ldap->cd($this->config->current['BASE']);
147 $ldap->search($entry['filter']);
148 if(!$ldap->success()){
149 $messages[] = sprintf(_("The given filter '%s' for entry %s seems to be invalid!"),
150 bold($entry['filter']), $nr);
151 }
152 }
153 }
155 return($messages);
156 }
159 /**
160 * Execute this plugin.
161 * @return string HTML to print.
162 */
163 public function execute ()
164 {
165 //
166 // Are we trying to modify state of this group ? If so,
167 // we can edit the current object.
168 //
169 if (isset($_POST['modify_state']))
170 {
171 $this->is_account = !$this->is_account;
172 }
174 //
175 // Display a message if this feature is disabled.
176 //
177 if (!$this->is_account)
178 {
179 return $this->show_disable_header(msgPool::addFeaturesButton(_("Dynamic object")), msgPool::featuresDisabled(_("Dynamic object")));
180 }
181 $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("Dynamic object")), msgPool::featuresEnabled(_("Dynamic object")));
183 // Display values.
184 //
185 $smarty = get_smarty();
186 $smarty->assign('labeledURIparsed', set_post($this->labeledURIparsed));
187 $smarty->assign('scopes', set_post($this->scopes));
188 $display .= $smarty->fetch(get_template_path('dyngroup.tpl', TRUE, dirname(__FILE__)));
189 return $display;
190 }
194 /**
195 * This plugin does nothing when this method is invoked.
196 */
197 public function remove_from_parent ()
198 {
199 parent::remove_from_parent();
200 $ldap = $this->config->get_ldap_link();
201 $ldap->cd($this->dn);
202 $ldap->modify($this->attrs);
203 if(!$ldap->success()){
204 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_DEL, get_class()));
205 }
206 return;
207 }
210 /**
211 * This function is called when tab is undisplayed. For example, the current user
212 * wants to change other settings of this group, but not save it to the LDAP
213 * directory directly.
214 */
215 public function save_object ()
216 {
217 parent::save_object();
219 // Add a new labeled Uri
220 if(isset($_POST['addUri'])){
221 $this->labeledURIparsed[] =
222 array(
223 'base' => 'ldap:///'.$this->dn,
224 'attr' => 'memberUid',
225 'scope' => 2,
226 'filter' => '(objectClass=posixGroup)');
227 }
229 // Remove a labeled Uri and get posts
230 foreach($this->labeledURIparsed as $key => $data){
231 foreach(array('scope','attr','filter','base') as $attr){
232 if(isset($_POST[$attr.'_'.$key])){
233 $this->labeledURIparsed[$key][$attr] = get_post($attr.'_'.$key);
234 }
235 }
237 // Remove labeled uri if requested
238 if(isset($_POST['delUri_'.$key])){
239 unset($this->labeledURIparsed[$key]);
240 }
241 }
242 $this->labeledURIparsed = array_values($this->labeledURIparsed);
243 }
246 /**
247 * That will add additionnal information into the current LDAP entry.
248 * If this plugin is disable, then it will remove any data that references
249 * this plugin into the LDAP directory.
250 * @return boolean
251 */
252 public function save ()
253 {
254 // Build up labeledUri entries
255 $this->labeledURI = array();
256 foreach($this->labeledURIparsed as $entry){
257 $scope = $this->scopes[$entry['scope']];
258 $filter = $entry['filter'];
259 $this->labeledURI[] = "{$entry['base']}?{$entry['attr']}?{$scope}?{$filter}";
260 }
261 $this->labeledURI = array_unique($this->labeledURI);
263 parent::save();
264 $this->cleanup();
265 $ldap = $this->config->get_ldap_link();
266 $ldap->cd($this->dn);
267 $ldap->modify($this->attrs);
268 if(!$ldap->success()){
269 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_DEL, get_class()));
270 }
271 }
274 /*! \brief Updates labeledURI entries in ldap.
275 * Check whether the given src_dn is part of some labeledURI entries
276 * and then updates the entries to use the dst_dn.
277 * @param $config The GOsa configuration object.
278 * @param $src_dn The source 'dn' of the object that was moved.
279 * @param $dst_dn The target 'dn' of the object that was moved.
280 */
281 public static function moveDynGroup($config,$src_dn,$dst_dn)
282 {
283 // Fetch all dynamic group definitions
284 $objs = get_list("(&(objectClass=labeledURIObject)(labeledURI=*))",array("all"),$config->current['BASE'],
285 array("dn","labeledURI"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
286 $newAttrs = array();
287 foreach($objs as $obj){
288 $changes = false;
289 $attrs = array();
290 for($i = 0; $i < $obj['labeledURI']['count']; $i++){
291 $c = $obj['labeledURI'][$i];
292 $c = preg_replace('/'.preg_quote($src_dn,'/').'/',$dst_dn,$c);
293 $attrs['labeledURI'][] = $c;
295 // Check if something has changed
296 if($c != $obj['labeledURI'][$i]){
297 $changes =TRUE;
298 }
299 }
301 // If at least one line of 'labeledURI' has changed then we have to update the whole entry.
302 if($changes) $newAttrs[$obj['dn']] = $attrs;
303 }
305 // If we've at least one entry to update then
306 if(count($newAttrs)){
307 $ldap = $config->get_ldap_link();
308 foreach($newAttrs as $dn => $data){
309 $ldap->cd($dn);
310 $ldap->modify($data);
311 if(!$ldap->success()){
312 trigger_error(sprintf("Failed to dynamic group object for %s: %s", bold($dn), $ldap->get_error()));
313 new log("debug",
314 "plugin/plugin::move()",$dn,array(),
315 " -- ERROR -- Failed to update dynamic groups (labeledURI) - ".$ldap->get_error());
316 }else{
317 new log("modify",
318 "plugin/plugin::move()",$dn,array_keys($data),
319 "Updated dynamic group entries (labeledURI)");
320 }
321 }
322 }
323 }
326 /**
327 * Static method to set ACL for this plugin.
328 */
329 public static function plInfo()
330 {
331 return Array(
332 "plShortName" => _("Dynamic object"),
333 "plDescription" => _("Dynamic object"),
334 "plSelfModify" => TRUE,
335 "plDepends" => Array(),
336 "plPriority" => 1,
337 "plSection" => Array("addon"),
338 "plCategory" => Array("groups", "department", "ogroups"),
339 "plProvidedAcls" => array(
340 'labeledURI' => _('Labeled URI'),
341 )
342 );
343 }
344 }
346 ?>