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