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 2014-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.Set; 026import java.util.TreeSet; 027 028import javax.naming.Context; 029import javax.naming.NamingEnumeration; 030import javax.naming.directory.SearchControls; 031import javax.naming.directory.SearchResult; 032import javax.naming.ldap.InitialLdapContext; 033 034import org.opends.admin.ads.util.ConnectionUtils; 035import org.opends.guitools.controlpanel.browser.BrowserController; 036import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; 037import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; 038import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 039import org.opends.guitools.controlpanel.ui.ProgressDialog; 040import org.opends.guitools.controlpanel.ui.nodes.BasicNode; 041import org.opends.guitools.controlpanel.util.Utilities; 042import org.forgerock.i18n.LocalizableMessage; 043import org.opends.server.config.ConfigConstants; 044import org.opends.server.tools.LDAPPasswordModify; 045import org.forgerock.opendj.ldap.DN; 046 047/** The task called when we want to reset the password of the user. */ 048public class ResetUserPasswordTask extends Task 049{ 050 private Set<String> backendSet; 051 private BasicNode node; 052 private char[] currentPassword; 053 private char[] newPassword; 054 private DN dn; 055 private boolean useAdminCtx; 056 057 /** 058 * Constructor of the task. 059 * @param info the control panel information. 060 * @param dlg the progress dialog where the task progress will be displayed. 061 * @param node the node corresponding to the entry whose password is going 062 * to be reset. 063 * @param controller the BrowserController. 064 * @param pwd the new password. 065 */ 066 public ResetUserPasswordTask(ControlPanelInfo info, ProgressDialog dlg, 067 BasicNode node, BrowserController controller, char[] pwd) 068 { 069 super(info, dlg); 070 backendSet = new HashSet<>(); 071 this.node = node; 072 this.newPassword = pwd; 073 dn = DN.valueOf(node.getDN()); 074 075 for (BackendDescriptor backend : info.getServerDescriptor().getBackends()) 076 { 077 for (BaseDNDescriptor baseDN : backend.getBaseDns()) 078 { 079 if (dn.isSubordinateOrEqualTo(baseDN.getDn())) 080 { 081 backendSet.add(backend.getBackendID()); 082 } 083 } 084 } 085 086 try 087 { 088 InitialLdapContext ctx = 089 controller.findConnectionForDisplayedEntry(node); 090 if (ctx != null && isBoundAs(dn, ctx)) 091 { 092 currentPassword = ConnectionUtils.getBindPassword(ctx).toCharArray(); 093 } 094 } 095 catch (Throwable t) 096 { 097 } 098 useAdminCtx = controller.isConfigurationNode(node); 099 } 100 101 @Override 102 public Type getType() 103 { 104 return Type.MODIFY_ENTRY; 105 } 106 107 @Override 108 public Set<String> getBackends() 109 { 110 return backendSet; 111 } 112 113 @Override 114 public LocalizableMessage getTaskDescription() 115 { 116 return INFO_CTRL_PANEL_RESET_USER_PASSWORD_TASK_DESCRIPTION.get( 117 node.getDN()); 118 } 119 120 @Override 121 public boolean regenerateDescriptor() 122 { 123 return false; 124 } 125 126 @Override 127 protected String getCommandLinePath() 128 { 129 return getCommandLinePath("ldappasswordmodify"); 130 } 131 132 @Override 133 protected ArrayList<String> getCommandLineArguments() 134 { 135 ArrayList<String> args = new ArrayList<>(); 136 if (currentPassword == null) 137 { 138 args.add("--authzID"); 139 args.add("dn:"+dn); 140 } 141 else 142 { 143 args.add("--currentPassword"); 144 args.add(String.valueOf(currentPassword)); 145 } 146 args.add("--newPassword"); 147 args.add(String.valueOf(newPassword)); 148 args.addAll(getConnectionCommandLineArguments(useAdminCtx, true)); 149 args.add(getNoPropertiesFileArgument()); 150 return args; 151 } 152 153 @Override 154 public boolean canLaunch(Task taskToBeLaunched, 155 Collection<LocalizableMessage> incompatibilityReasons) 156 { 157 if (!isServerRunning() 158 && state == State.RUNNING 159 && runningOnSameServer(taskToBeLaunched)) 160 { 161 // All the operations are incompatible if they apply to this 162 // backend for safety. This is a short operation so the limitation 163 // has not a lot of impact. 164 Set<String> backends = new TreeSet<>(taskToBeLaunched.getBackends()); 165 backends.retainAll(getBackends()); 166 if (!backends.isEmpty()) 167 { 168 incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched)); 169 return false; 170 } 171 } 172 return true; 173 } 174 175 @Override 176 public void runTask() 177 { 178 state = State.RUNNING; 179 lastException = null; 180 try 181 { 182 ArrayList<String> arguments = getCommandLineArguments(); 183 String[] args = new String[arguments.size()]; 184 arguments.toArray(args); 185 186 returnCode = LDAPPasswordModify.mainPasswordModify(args, false, 187 outPrintStream, errorPrintStream); 188 189 if (returnCode != 0) 190 { 191 state = State.FINISHED_WITH_ERROR; 192 } 193 else 194 { 195 if (lastException == null && currentPassword != null) 196 { 197 // The connections must be updated, just update the environment, which 198 // is what we use to clone connections and to launch scripts. 199 // The environment will also be used if we want to reconnect. 200 getInfo().getConnection().getLdapContext().addToEnvironment( 201 Context.SECURITY_CREDENTIALS, 202 String.valueOf(newPassword)); 203 if (getInfo().getUserDataDirContext() != null) 204 { 205 getInfo().getUserDataDirContext().addToEnvironment( 206 Context.SECURITY_CREDENTIALS, 207 String.valueOf(newPassword)); 208 } 209 } 210 state = State.FINISHED_SUCCESSFULLY; 211 } 212 } 213 catch (Throwable t) 214 { 215 lastException = t; 216 state = State.FINISHED_WITH_ERROR; 217 } 218 } 219 220 /** 221 * Returns <CODE>true</CODE> if we are bound using the provided entry. In 222 * the case of root entries this is not necessarily the same as using that 223 * particular DN (we might be binding using a value specified in 224 * ds-cfg-alternate-bind-dn). 225 * @param dn the DN. 226 * @param ctx the connection that we are using to modify the password. 227 * @return <CODE>true</CODE> if we are bound using the provided entry. 228 */ 229 private boolean isBoundAs(DN dn, InitialLdapContext ctx) 230 { 231 boolean isBoundAs = false; 232 DN bindDN = DN.rootDN(); 233 try 234 { 235 String b = ConnectionUtils.getBindDN(ctx); 236 bindDN = DN.valueOf(b); 237 isBoundAs = dn.equals(bindDN); 238 } 239 catch (Throwable t) 240 { 241 // Ignore 242 } 243 if (!isBoundAs) 244 { 245 try 246 { 247 SearchControls ctls = new SearchControls(); 248 ctls.setSearchScope(SearchControls.OBJECT_SCOPE); 249 String filter = 250 "(|(objectClass=*)(objectclass=ldapsubentry))"; 251 String attrName = ConfigConstants.ATTR_ROOTDN_ALTERNATE_BIND_DN; 252 ctls.setReturningAttributes(new String[] {attrName}); 253 NamingEnumeration<SearchResult> entries = 254 ctx.search(Utilities.getJNDIName(dn.toString()), filter, ctls); 255 256 try 257 { 258 while (entries.hasMore()) 259 { 260 SearchResult sr = entries.next(); 261 Set<String> dns = ConnectionUtils.getValues(sr, attrName); 262 for (String sDn : dns) 263 { 264 if (bindDN.equals(DN.valueOf(sDn))) 265 { 266 isBoundAs = true; 267 break; 268 } 269 } 270 } 271 } 272 finally 273 { 274 entries.close(); 275 } 276 } 277 catch (Throwable t) 278 { 279 } 280 } 281 return isBoundAs; 282 } 283}