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 *      Copyright 2012-2014 ForgeRock AS
024 *
025 */
026
027package org.forgerock.opendj.examples;
028
029import java.util.Collection;
030
031import org.forgerock.opendj.ldap.Connection;
032import org.forgerock.opendj.ldap.LdapException;
033import org.forgerock.opendj.ldap.LDAPConnectionFactory;
034import org.forgerock.opendj.ldap.ModificationType;
035import org.forgerock.opendj.ldap.ResultCode;
036import org.forgerock.opendj.ldap.RootDSE;
037import org.forgerock.opendj.ldap.controls.PermissiveModifyRequestControl;
038import org.forgerock.opendj.ldap.requests.CompareRequest;
039import org.forgerock.opendj.ldap.requests.ModifyRequest;
040import org.forgerock.opendj.ldap.requests.Requests;
041import org.forgerock.opendj.ldap.responses.CompareResult;
042
043/**
044 * This command-line client demonstrates adding and removing a member from a
045 * (potentially large) static group.
046 *
047 * The client takes as arguments the host and port of the directory server, the
048 * group DN, the member DN, and whether to "add" or "del" the specified member
049 * from the group. The client uses the Permissive Modify control if it is
050 * available to avoid having to check whether the member belongs to the group or
051 * not.
052 *
053 * This client expects a group that is a <code>groupOfNames</code> such as:
054 *
055 * <pre>
056 * dn: cn=My Static Group,ou=Groups,dc=example,dc=com
057 * cn: My Static Group
058 * objectClass: groupOfNames
059 * objectClass: top
060 * ou: Groups
061 * member: uid=ahunter,ou=People,dc=example,dc=com
062 * member: uid=bjensen,ou=People,dc=example,dc=com
063 * member: uid=tmorris,ou=People,dc=example,dc=com
064 * </pre>
065 *
066 * This client connects as <code>cn=Directory Manager</code> with password
067 * <code>password</code>. Not a best practice; in real code use application
068 * specific credentials to connect, and ensure that your application has access
069 * to use the Permissive Modify control if your directory server supports it.
070 */
071public final class UpdateGroup {
072
073    /**
074     * Connect to the directory server to update the group.
075     *
076     * @param args
077     *            The command line arguments: host, port, group-dn, member-dn,
078     *            {add|del}
079     */
080    public static void main(String[] args) {
081        if (args.length != 5) {
082            printUsage();
083        }
084        final String host = args[0];
085        final int port = Integer.parseInt(args[1]);
086        final String groupDN = args[2];
087        final String memberDN = args[3];
088        final ModificationType modType = getModificationType(args[4]);
089
090        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
091        Connection connection = null;
092        try {
093            connection = factory.getConnection();
094
095            Collection<String> controls =
096                    RootDSE.readRootDSE(connection).getSupportedControls();
097
098            final String user = "cn=Directory Manager";
099            final char[] password = "password".toCharArray();
100            connection.bind(user, password);
101
102            // --- JCite permissive ---
103            if (controls.contains(PermissiveModifyRequestControl.OID)) {
104
105                final ModifyRequest request = Requests.newModifyRequest(groupDN)
106                        .addControl(PermissiveModifyRequestControl.newControl(true))
107                        .addModification(modType, "member", memberDN);
108                connection.modify(request);
109
110            } else {
111                // --- JCite permissive ---
112
113                // --- JCite without permissive ---
114                System.out.println("Checking whether the entry with DN "
115                        + memberDN + " belongs to the group with DN " + groupDN
116                        + "...");
117                final CompareRequest request =
118                        Requests.newCompareRequest(groupDN, "member", memberDN);
119                CompareResult result = connection.compare(request);
120
121                if (modType == ModificationType.ADD
122                        && result.getResultCode() == ResultCode.COMPARE_FALSE) {
123                    System.out.println("Member does not yet belong to group."
124                            + " Adding it...");
125                    final ModifyRequest addMember =
126                            Requests.newModifyRequest(groupDN)
127                                .addModification(modType, "member", memberDN);
128                    connection.modify(addMember);
129                }
130
131                if (modType == ModificationType.DELETE
132                        && result.getResultCode() == ResultCode.COMPARE_TRUE) {
133                    System.out.println("Member belongs to group."
134                            + " Removing it...");
135                    final ModifyRequest delMember =
136                            Requests.newModifyRequest(groupDN)
137                                .addModification(modType, "member", memberDN);
138                    connection.modify(delMember);
139                }
140                // --- JCite without permissive ---
141            }
142
143            String op = (modType == ModificationType.ADD) ? "added to" : "deleted from";
144            System.out.println("The entry with DN " + memberDN + " has been "
145                    + op + " the group with DN " + groupDN + ".");
146
147        } catch (final LdapException e) {
148            System.err.println(e.getMessage());
149            System.exit(e.getResult().getResultCode().intValue());
150            return;
151        } finally {
152            if (connection != null) {
153                connection.close();
154            }
155        }
156    }
157
158    /**
159     * Return the modification type for the update operation.
160     * @param operation Operation specified as an argument (add or del).
161     */
162    private static ModificationType getModificationType(String operation) {
163        final boolean isAdd = "add".equalsIgnoreCase(operation);
164        if (!isAdd && !"del".equalsIgnoreCase(operation)) {
165            printUsage();
166        }
167        return isAdd ? ModificationType.ADD : ModificationType.DELETE;
168    }
169
170    /**
171     * Print usage then exit.
172     */
173    private static void printUsage() {
174        System.err.println("Usage: host port group-dn member-dn {add|del}");
175        System.err.println("For example: localhost 1389 "
176                + "cn=Static,ou=Groups,dc=example,dc=com "
177                + "uid=user.5150,ou=People,dc=example,dc=com "
178                + "del");
179        System.exit(1);
180    }
181
182    /**
183     * Constructor not used.
184     */
185    private UpdateGroup() {
186        // Not used.
187    }
188}