001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027
028package org.opends.guitools.controlpanel.task;
029
030import static org.opends.messages.AdminToolMessages.*;
031
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.HashSet;
035import java.util.LinkedHashSet;
036import java.util.Set;
037import java.util.TreeSet;
038
039import javax.naming.NamingEnumeration;
040import javax.naming.NamingException;
041import javax.naming.directory.Attribute;
042import javax.naming.directory.BasicAttribute;
043import javax.naming.directory.DirContext;
044import javax.naming.directory.ModificationItem;
045import javax.naming.directory.SearchControls;
046import javax.naming.directory.SearchResult;
047import javax.swing.SwingUtilities;
048
049import org.opends.admin.ads.util.ConnectionUtils;
050import org.opends.guitools.controlpanel.browser.BrowserController;
051import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
052import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
053import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
054import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
055import org.opends.guitools.controlpanel.ui.ProgressDialog;
056import org.opends.guitools.controlpanel.util.Utilities;
057import org.opends.messages.AdminToolMessages;
058import org.forgerock.i18n.LocalizableMessage;
059import org.opends.server.types.DN;
060import org.opends.server.util.ServerConstants;
061
062/**
063 * The class that is in charge of adding a set of entries to a set of static
064 * groups.
065 */
066public class AddToGroupTask extends Task
067{
068  private Set<String> backendSet;
069  private LinkedHashSet<DN> dns = new LinkedHashSet<>();
070  private LinkedHashSet<DN> groupDns = new LinkedHashSet<>();
071
072  /**
073   * Constructor of the task.
074   * @param info the control panel information.
075   * @param dlg the progress dialog where the task progress will be displayed.
076   * @param dns the DNs of the entries we want to add to the groups.
077   * @param groupDns the groups that we want to modify.
078   */
079  public AddToGroupTask(ControlPanelInfo info, ProgressDialog dlg,
080      Set<DN> dns, Set<DN> groupDns)
081  {
082    super(info, dlg);
083    backendSet = new HashSet<>();
084    this.dns.addAll(dns);
085    this.groupDns.addAll(groupDns);
086    for (DN groupDn : groupDns)
087    {
088      for (BackendDescriptor backend :
089        info.getServerDescriptor().getBackends())
090      {
091        for (BaseDNDescriptor baseDN : backend.getBaseDns())
092        {
093          if (groupDn.isDescendantOf(baseDN.getDn()))
094          {
095            backendSet.add(backend.getBackendID());
096          }
097        }
098      }
099    }
100  }
101
102  /** {@inheritDoc} */
103  public Type getType()
104  {
105    return Type.MODIFY_ENTRY;
106  }
107
108  /** {@inheritDoc} */
109  public Set<String> getBackends()
110  {
111    return backendSet;
112  }
113
114  /** {@inheritDoc} */
115  public LocalizableMessage getTaskDescription()
116  {
117    return AdminToolMessages.INFO_CTRL_PANEL_ADD_TO_GROUP_TASK_DESCRIPTION.get();
118  }
119
120  /** {@inheritDoc} */
121  protected String getCommandLinePath()
122  {
123    return null;
124  }
125
126  /** {@inheritDoc} */
127  protected ArrayList<String> getCommandLineArguments()
128  {
129    return new ArrayList<>();
130  }
131
132  /** {@inheritDoc} */
133  public boolean canLaunch(Task taskToBeLaunched,
134      Collection<LocalizableMessage> incompatibilityReasons)
135  {
136    if (!isServerRunning()
137        && state == State.RUNNING
138        && runningOnSameServer(taskToBeLaunched))
139    {
140      // All the operations are incompatible if they apply to this
141      // backend for safety.  This is a short operation so the limitation
142      // has not a lot of impact.
143      Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends());
144      backends.retainAll(getBackends());
145      if (!backends.isEmpty())
146      {
147        incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched));
148        return false;
149      }
150    }
151    return true;
152  }
153
154  /** {@inheritDoc} */
155  public boolean regenerateDescriptor()
156  {
157    return false;
158  }
159
160  /** {@inheritDoc} */
161  public void runTask()
162  {
163    state = State.RUNNING;
164    lastException = null;
165
166    try
167    {
168      for (final DN groupDn : groupDns)
169      {
170        final Collection<ModificationItem> modifications =
171          getModifications(groupDn, dns);
172        if (!modifications.isEmpty())
173        {
174          ModificationItem[] mods =
175          new ModificationItem[modifications.size()];
176          modifications.toArray(mods);
177
178          SwingUtilities.invokeLater(new Runnable()
179          {
180            public void run()
181            {
182              printEquivalentCommandToModify(groupDn, modifications, false);
183              getProgressDialog().appendProgressHtml(
184                  Utilities.getProgressWithPoints(
185                      INFO_CTRL_PANEL_ADDING_TO_GROUP.get(groupDn),
186                      ColorAndFontConstants.progressFont));
187            }
188          });
189
190          getInfo().getDirContext().modifyAttributes(
191              Utilities.getJNDIName(groupDn.toString()), mods);
192
193          SwingUtilities.invokeLater(new Runnable()
194          {
195            public void run()
196            {
197              getProgressDialog().appendProgressHtml(
198                  Utilities.getProgressDone(
199                      ColorAndFontConstants.progressFont));
200            }
201          });
202        }
203      }
204      state = State.FINISHED_SUCCESSFULLY;
205    }
206    catch (Throwable t)
207    {
208      lastException = t;
209      state = State.FINISHED_WITH_ERROR;
210    }
211  }
212
213  /**
214   * Returns the modifications that must be made to the provided group.
215   * @param groupDn the DN of the static group that must be updated.
216   * @param dns the list of entry DNs that must be added to the group.
217   * @return the list of modifications (in form of ModificationItem) that
218   *  must be made to the provided group.
219   * @throws NamingException if an error occurs.
220   */
221  private Collection<ModificationItem> getModifications(DN groupDn,
222  Set<DN> dns) throws NamingException
223  {
224    ArrayList<ModificationItem> modifications = new ArrayList<>();
225
226    // Search for the group entry
227    SearchControls ctls = new SearchControls();
228    ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
229    ctls.setReturningAttributes(
230        new String[] {
231            ServerConstants.OBJECTCLASS_ATTRIBUTE_TYPE_NAME,
232            ServerConstants.ATTR_MEMBER,
233            ServerConstants.ATTR_UNIQUE_MEMBER
234        });
235    String filter = BrowserController.ALL_OBJECTS_FILTER;
236    NamingEnumeration<SearchResult> result =
237      getInfo().getDirContext().search(
238          Utilities.getJNDIName(groupDn.toString()),
239          filter, ctls);
240
241    try
242    {
243      String memberAttr = ServerConstants.ATTR_MEMBER;
244      while (result.hasMore())
245      {
246        SearchResult sr = result.next();
247        Set<String> objectClasses =
248          ConnectionUtils.getValues(sr, ServerConstants
249            .OBJECTCLASS_ATTRIBUTE_TYPE_NAME);
250        if (objectClasses.contains(ServerConstants.OC_GROUP_OF_UNIQUE_NAMES))
251        {
252          memberAttr = ServerConstants.ATTR_UNIQUE_MEMBER;
253        }
254        Set<String> values = ConnectionUtils.getValues(sr, memberAttr);
255        Set<String> dnsToAdd = new LinkedHashSet<>();
256        if (values != null)
257        {
258          for (DN newDn : dns)
259          {
260            boolean found = false;
261            for (String dn : values)
262            {
263              if (Utilities.areDnsEqual(dn, newDn.toString()))
264              {
265                found = true;
266                break;
267              }
268            }
269            if (!found)
270            {
271              dnsToAdd.add(newDn.toString());
272            }
273          }
274        }
275        else
276        {
277          for (DN newDn : dns)
278          {
279            dnsToAdd.add(newDn.toString());
280          }
281        }
282        if (!dnsToAdd.isEmpty())
283        {
284          Attribute attribute = new BasicAttribute(memberAttr);
285          for (String dn : dnsToAdd)
286          {
287            attribute.add(dn);
288          }
289          modifications.add(new ModificationItem(
290              DirContext.ADD_ATTRIBUTE,
291              attribute));
292        }
293      }
294    }
295    finally
296    {
297      result.close();
298    }
299    return modifications;
300  }
301}
302