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