001 /*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
005 *
006 * The contents of this file are subject to the terms of either the GNU
007 * General Public License Version 2 only ("GPL") or the Common Development
008 * and Distribution License("CDDL") (collectively, the "License"). You
009 * may not use this file except in compliance with the License. You can
010 * obtain a copy of the License at
011 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
012 * or packager/legal/LICENSE.txt. See the License for the specific
013 * language governing permissions and limitations under the License.
014 *
015 * When distributing the software, include this License Header Notice in each
016 * file and include the License file at packager/legal/LICENSE.txt.
017 *
018 * GPL Classpath Exception:
019 * Oracle designates this particular file as subject to the "Classpath"
020 * exception as provided by Oracle in the GPL Version 2 section of the License
021 * file that accompanied this code.
022 *
023 * Modifications:
024 * If applicable, add the following below the License Header, with the fields
025 * enclosed by brackets [] replaced by your own identifying information:
026 * "Portions Copyright [year] [name of copyright owner]"
027 *
028 * Contributor(s):
029 * If you wish your version of this file to be governed by only the CDDL or
030 * only the GPL Version 2, indicate your decision by adding "[Contributor]
031 * elects to include this software in this distribution under the [CDDL or GPL
032 * Version 2] license." If you don't indicate a single choice of license, a
033 * recipient has the option to distribute your version of this file under
034 * either the CDDL, the GPL Version 2 or to extend the choice of license to
035 * its licensees as provided above. However, if you add GPL Version 2 code
036 * and therefore, elected the GPL Version 2 license, then the option applies
037 * only if the new code is made subject to such option by the copyright
038 * holder.
039 */
040
041 package com.sun.enterprise.admin.cli;
042
043 import com.sun.enterprise.admin.launcher.GFLauncher;
044 import com.sun.enterprise.admin.launcher.GFLauncherException;
045 import com.sun.enterprise.admin.launcher.GFLauncherFactory;
046 import com.sun.enterprise.admin.launcher.GFLauncherInfo;
047 import java.io.Console;
048 import org.jvnet.hk2.annotations.*;
049 import org.jvnet.hk2.component.*;
050 import org.glassfish.api.admin.*;
051 import com.sun.enterprise.admin.remote.RemoteAdminCommand;
052 import com.sun.enterprise.config.serverbeans.SecureAdmin;
053 import com.sun.enterprise.util.SystemPropertyConstants;
054 import com.sun.enterprise.universal.i18n.LocalStringsImpl;
055 import com.sun.enterprise.universal.xml.MiniXmlParser;
056 import com.sun.enterprise.universal.xml.MiniXmlParserException;
057 import com.sun.enterprise.util.net.NetUtils;
058 import java.io.IOException;
059 import java.net.ConnectException;
060 import org.glassfish.api.Param;
061 import org.glassfish.security.common.FileRealmHelper;
062
063 /**
064 * The change-admin-password command.
065 * The remote command implementation presents a different
066 * interface (set of options) than the local command.
067 * This special local implementation adapts the local
068 * interface to the requirements of the remote command.
069 *
070 * The remote command is different in that it accepts the user name as
071 * an operand. This command accepts it via the --user parameter. If the --user
072 * option isn't specified, this command prompts for the user name.
073 *
074 * Another difference is that the local command will prompt for the old
075 * password only once. The default behavior for @Param for passwords is to
076 * prompt for the password twice. *
077 *
078 * @author Bill Shannon
079 */
080 @Service(name = "change-admin-password")
081 @Scoped(PerLookup.class)
082 @ExecuteOn({RuntimeType.DAS})
083 public class ChangeAdminPasswordCommand extends LocalDomainCommand {
084 private ParameterMap params;
085
086
087 private static final LocalStringsImpl strings =
088 new LocalStringsImpl(ChangeAdminPasswordCommand.class);
089
090 private static final String oldpwName = Environment.AS_ADMIN_ENV_PREFIX + "PASSWORD";
091 private static final String newpwName = Environment.AS_ADMIN_ENV_PREFIX + "NEWPASSWORD";
092
093 @Param(name = "domain_name", optional = true)
094 private String userArgDomainName;
095
096 private SecureAdmin secureAdmin = null;
097
098
099
100 /**
101 * Require the user to actually type the passwords unless they are in
102 * the file specified by the --passwordfile option.
103 */
104 @Override
105 protected void validate()
106 throws CommandException, CommandValidationException {
107 setDomainName(userArgDomainName);
108 super.validate();
109 /*
110 * If --user wasn't specified as a program option,
111 * we treat it as a required option and prompt for it
112 * if possible.
113 */
114 if (programOpts.getUser() == null) {
115 // prompt for it (if interactive)
116 Console cons = System.console();
117 if (cons != null && programOpts.isInteractive()) {
118 cons.printf("%s", strings.get("AdminUserDefaultPrompt",
119 SystemPropertyConstants.DEFAULT_ADMIN_USER));
120 String val = cons.readLine();
121 if (ok(val))
122 programOpts.setUser(val);
123 else
124 programOpts.setUser(
125 SystemPropertyConstants.DEFAULT_ADMIN_USER);
126 } else {
127 //logger.info(strings.get("AdminUserRequired"));
128 throw new CommandValidationException(
129 strings.get("AdminUserRequired"));
130 }
131 }
132
133 // now, prompt for the passwords
134 try {
135 String password = getPasswords();
136 programOpts.setPassword(password,
137 ProgramOptions.PasswordLocation.USER);
138 } catch (CommandValidationException cve) {
139 throw new CommandException(cve);
140 }
141
142 /*
143 * Now that the user-supplied parameters have been validated,
144 * we set the parameter values for the remote command.
145 */
146 params = new ParameterMap();
147 params.set("DEFAULT", programOpts.getUser());
148 params.set(oldpwName, passwords.get(oldpwName));
149 params.set(newpwName, passwords.get(newpwName));
150 }
151
152 /**
153 * Execute the remote command using the parameters we've collected.
154 */
155 @Override
156 protected int executeCommand() throws CommandException {
157
158 if(ok(domainDirParam) || ok(userArgDomainName)) {
159 //If domaindir or domain arguments are provided,
160 // do not attempt remote connection. Change password locally
161 String domainDir = (ok(domainDirParam))?domainDirParam:getDomainsDir().getPath();
162 String domainName = (ok(userArgDomainName))?userArgDomainName:getDomainName();
163 return changeAdminPasswordLocally(domainDir,domainName);
164
165 } else {
166 try {
167 RemoteAdminCommand rac = new RemoteAdminCommand(name,
168 programOpts.getHost(), programOpts.getPort(),
169 programOpts.isSecure(), programOpts.getUser(),
170 programOpts.getPassword(), logger);
171 rac.executeCommand(params);
172 return SUCCESS;
173 } catch(CommandException ce) {
174 if ( ce.getCause() instanceof ConnectException) {
175 //Remote change failure - change password with default values of
176 // domaindir and domain name,if the --host option is not provided.
177 if(!isLocalHost(programOpts.getHost())) {
178 throw ce;
179 }
180 return changeAdminPasswordLocally(getDomainsDir().getPath(),
181 getDomainName());
182
183
184 } else {
185 throw ce;
186 }
187 }
188 }
189
190
191 }
192
193 /**
194 * Prompt for all the passwords needed by this command.
195 * Return the old password.
196 */
197 private String getPasswords() throws CommandValidationException {
198 String oldpassword = passwords.get(oldpwName);
199 if (oldpassword == null) {
200 oldpassword = readPassword(strings.get("AdminPasswordPrompt"));
201 }
202
203 String newpassword = passwords.get(newpwName);
204 if (newpassword == null) {
205 newpassword = readPassword(strings.get("AdminNewPasswordPrompt"));
206 String newpasswordAgain =
207 readPassword(strings.get("AdminNewPasswordConfirmationPrompt"));
208 if (!newpassword.equals(newpasswordAgain)) {
209 throw new CommandValidationException(
210 strings.get("OptionsDoNotMatch", "Admin Password"));
211 }
212 }
213
214 passwords.put(oldpwName, oldpassword);
215 passwords.put(newpwName, newpassword);
216 return oldpassword;
217 }
218
219 private int changeAdminPasswordLocally(String domainDir, String domainName) throws CommandException {
220
221 if(!isLocalHost(programOpts.getHost())) {
222 throw new CommandException(strings.get("CannotExecuteLocally"));
223 }
224
225 GFLauncher launcher = null;
226 try {
227 launcher = GFLauncherFactory.getInstance(RuntimeType.DAS);
228 GFLauncherInfo info = launcher.getInfo();
229 info.setDomainName(domainName);
230 info.setDomainParentDir(domainDir);
231 launcher.setup();
232
233 //If secure admin is enabled and if new password is null
234 //throw new exception
235 if(launcher.isSecureAdminEnabled()) {
236 String newPassword = (String) passwords.get(newpwName);
237 if ((newPassword == null) || (newPassword.isEmpty())) {
238 throw new CommandException(strings.get("NullNewPassword"));
239 }
240 }
241
242 String adminKeyFile = launcher.getAdminRealmKeyFile();
243
244 if (adminKeyFile != null) {
245 //This is a FileRealm, instantiate it.
246 FileRealmHelper helper = new FileRealmHelper(adminKeyFile);
247
248 //Authenticate the old password if the user does not have RESET
249 if(helper.hasAuthenticatableUser()) {
250 String[] groups = helper.authenticate(programOpts.getUser(), ((String) passwords.get(oldpwName)).toCharArray());
251 if (groups == null) {
252 throw new CommandException(strings.get("InvalidCredentials", programOpts.getUser()));
253 }
254 }
255 helper.updateUser(programOpts.getUser(), programOpts.getUser(), ((String) passwords.get(newpwName)).toCharArray(), null);
256 helper.persist();
257 return SUCCESS;
258
259 } else {
260 //Cannot change password locally for non file realms
261 throw new CommandException(strings.get("NotFileRealmCannotChangeLocally"));
262
263 }
264
265 } catch (MiniXmlParserException ex) {
266 throw new CommandException(ex);
267 } catch (GFLauncherException ex) {
268 throw new CommandException(ex);
269 } catch (IOException ex) {
270 throw new CommandException(ex);
271 }
272 }
273
274
275 private static boolean isLocalHost(String host) {
276 if(host != null && (NetUtils.isThisHostLocal(host) || NetUtils.isLocal(host))) {
277 return true;
278 }
279 return false;
280 }
281
282
283
284 }