1 /*******************************************************************************
2  * Copyright (c) 2011 SAP AG
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Lazar Kirchev, SAP AG - initial API and implementation
13  *******************************************************************************/
14 
15 package org.eclipse.equinox.console.ssh;
16 
17 import java.io.BufferedReader;
18 import java.io.IOException;
19 import java.io.InputStreamReader;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 
23 import org.apache.felix.service.command.CommandSession;
24 import org.apache.felix.service.command.Descriptor;
25 import org.eclipse.equinox.console.common.ConsoleInputScanner;
26 import org.eclipse.equinox.console.common.Scanner;
27 import org.eclipse.equinox.console.storage.DigestUtil;
28 import org.eclipse.equinox.console.storage.SecureUserStore;
29 
30 /**
31  * This class provides commands for administering users: adding, removing and listing users; setting or changing password;
32  * resetting password; adding and removing roles
33  *
34  *
35  */
36 public class UserAdminCommand {
37 	private static final String INPUT_SCANNER = "INPUT_SCANNER";
38 	private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER";
39 	private static final String DEFAULT_USER = "equinox";
40 	private static final int MINIMAL_PASSWORD_LENGTH = 8;
41 	private static final int PASSWORD_INPUT_TRIALS_LIMIT = 3;
42 
43 	/**
44 	 * Command for adding a user
45 	 *
46 	 * @param args command line arguments in the format -username <username> -password <password> -roles <comma-separated list of user roles (optional)>
47 	 * @throws Exception
48 	 */
49 	@Descriptor("Add user with password and roles")
addUser(@escriptorR) String[] args)50 	public void addUser(@Descriptor("-username <username>\r\n-password <password>\r\n-roles <comma-separated list of user roles (optional)>") String[] args) throws Exception {
51 		String username = null;
52 		String password = null;
53 		String roles = "";
54 
55 		for (int i = 0; i < args.length; i++) {
56 			if ("-username".equals(args[i]) && i < args.length - 1) {
57 				username = args[i + 1];
58 				i++;
59 			} else if ("-password".equals(args[i]) && i < args.length - 1) {
60 				password = args[i + 1];
61 				i++;
62 			} else if ("-roles".equals(args[i]) && i < args.length - 1) {
63 				roles = args[i + 1];
64 				i++;
65 			}
66 		}
67 
68 		if (! validateUsername(username)) {
69 			throw new Exception("Invalid username");
70 		}
71 
72 		if (password == null) {
73 			throw new Exception("Password not specified");
74 		}
75 
76 		if (password.length() < MINIMAL_PASSWORD_LENGTH) {
77 			throw new Exception("Password should be at least 8 characters");
78 		}
79 
80 		SecureUserStore.putUser(username, DigestUtil.encrypt(password), roles);
81 
82 		if(SecureUserStore.existsUser(DEFAULT_USER)) {
83 			SecureUserStore.deleteUser(DEFAULT_USER);
84 		}
85 	}
86 
87 	/**
88 	 * Command for setting or changing the password of a user.
89 	 *
90 	 * @param args command-line arguments in the format -username <username> -password <password>
91 	 * @throws Exception
92 	 */
93 	@Descriptor("Set or change password")
setPassword(@escriptorR) String[] args)94 	public void setPassword(@Descriptor("-username <username>\r\n-password <password>") String[] args) throws Exception {
95 		String username = null;
96 		String password = null;
97 
98 		for (int i = 0; i < args.length; i++) {
99 			if ("-username".equals(args[i]) && i < args.length - 1) {
100 				username = args[i + 1];
101 				i++;
102 			} else if ("-password".equals(args[i]) && i < args.length - 1) {
103 				password = args[i + 1];
104 				i++;
105 			}
106 		}
107 
108 		if (! validateUsername(username)) {
109 			throw new Exception("Invalid username");
110 		}
111 
112 		if (password == null) {
113 			throw new Exception("Password not specified");
114 		}
115 
116 		if (password.length() < MINIMAL_PASSWORD_LENGTH) {
117 			throw new Exception("Password should be at least 8 characters");
118 		}
119 
120 		SecureUserStore.setPassword(username, DigestUtil.encrypt(password));
121 	}
122 
123 	/**
124 	 * Command for adding a user. The command interactively asks for username, password and roles; the
125 	 * input plain text password is encrypted before storing.
126 	 *
127 	 * @param session
128 	 * @return true if the user was successfully added
129 	 *
130 	 * @throws Exception
131 	 */
132 	@Descriptor("Add user with password and roles interactively")
addUser(final CommandSession session)133 	public boolean addUser(final CommandSession session) throws Exception {
134 
135 		ConsoleInputScanner inputScanner = (ConsoleInputScanner) session.get(INPUT_SCANNER);
136 		Scanner scanner = (Scanner) session.get(SSH_INPUT_SCANNER);
137 
138 		try {
139 			// switch off the history so that username, password and roles will not be saved in console history
140 			if (scanner != null) {
141 				inputScanner.toggleHistoryEnabled(false);
142 			}
143 			BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
144 			String username = readUsername(reader);
145 			if (!validateUsername(username)) {
146 				System.out.println("Invalid username");
147 				return false;
148 			}
149 
150 			if (SecureUserStore.existsUser(username)) {
151 				System.out.println("Username already exists");
152 				return false;
153 			}
154 
155 			// switch off the echo so that the password will not be printed in the console
156 			if (scanner != null) {
157 				scanner.toggleEchoEnabled(false);
158 			}
159 			String password = readPassword(reader);
160 			if (password == null){
161 				return false;
162 			}
163 			if (scanner != null) {
164 				scanner.toggleEchoEnabled(true);
165 			}
166 
167 			String roles = readRoles(reader);
168 			if (roles == null) {
169 				return false;
170 			}
171 
172 			SecureUserStore.putUser(username, DigestUtil.encrypt(password), roles);
173 
174 			if(SecureUserStore.existsUser(DEFAULT_USER)) {
175 				SecureUserStore.deleteUser(DEFAULT_USER);
176 			}
177 		} finally {
178 			if (scanner != null) {
179 				inputScanner.toggleHistoryEnabled(true);
180 				scanner.toggleEchoEnabled(true);
181 			}
182 		}
183 
184 		return true;
185 	}
186 
187 	@Descriptor("Delete user")
deleteUser(@escriptorR) String username)188 	public void deleteUser(@Descriptor("username of the user to be deleted") String username) {
189 		if (SecureUserStore.existsUser(username)) {
190 			SecureUserStore.deleteUser(username);
191 		}
192 	}
193 
194 	/**
195 	 * Command to remove the password for a user
196 	 *
197 	 * @param username user to remove the password for
198 	 * @throws Exception
199 	 */
200 	@Descriptor("Reset password")
resetPassword(@escriptorR) String username)201 	public void resetPassword(@Descriptor("username of the user whose password will be reset") String username) throws Exception {
202 		if (!SecureUserStore.existsUser(username)) {
203 			throw new Exception("Such user does not exist");
204 		}
205 
206 		SecureUserStore.resetPassword(username);
207 	}
208 
209 	/**
210 	 * Command to set or change the password for a user; the command asks interactively for the new password; the
211 	 * input plain text password is encrypted before storing.
212 	 *
213 	 * @param session
214 	 * @param username the user whose password will be changed
215 	 * @throws Exception
216 	 */
217 	@Descriptor("Set or change password")
setPassword(final CommandSession session, @Descriptor(R) String username)218 	public void setPassword(final CommandSession session, @Descriptor("Username of the user whose password will be changed") String username) throws Exception {
219 		if ("".equals(username)) {
220 			System.out.println("Username not specified");
221 			return;
222 		}
223 
224 		if (!SecureUserStore.existsUser(username)) {
225 			throw new Exception("Such user does not exist");
226 		}
227 
228 		ConsoleInputScanner inputScanner = (ConsoleInputScanner) session.get(INPUT_SCANNER);
229 		Scanner scanner = (Scanner) session.get(SSH_INPUT_SCANNER);
230 
231 		try {
232 			// switch off echo and history so that the password is neither echoed to the console, nor saved in history
233 			if (scanner != null) {
234 				inputScanner.toggleHistoryEnabled(false);
235 				scanner.toggleEchoEnabled(false);
236 			}
237 
238 			BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
239 			String password = readPassword(reader);
240 			if (password == null) {
241 				return;
242 			}
243 
244 			SecureUserStore.setPassword(username, DigestUtil.encrypt(password));
245 		} finally {
246 			if (scanner != null) {
247 				inputScanner.toggleHistoryEnabled(true);
248 				scanner.toggleEchoEnabled(true);
249 			}
250 		}
251 	}
252 
253 	/**
254 	 * Command to add roles to a user
255 	 *
256 	 * @param args command line arguments in the format -username <username>\r\n-roles <comma-separated list of roles to add>
257 	 * @throws Exception
258 	 */
259 	@Descriptor("Add roles to user")
addRoles(@escriptorR) String[] args)260 	public void addRoles(@Descriptor("-username <username>\r\n-roles <comma-separated list of roles to add>") String[] args) throws Exception {
261 		String username = null;
262 		String roles = "";
263 
264 		for (int i = 0; i < args.length; i++) {
265 			if ("-username".equals(args[i]) && i < args.length - 1) {
266 				username = args[i + 1];
267 				i++;
268 			} else if ("-roles".equals(args[i]) && i < args.length - 1) {
269 				roles = args[i + 1];
270 				i++;
271 			}
272 		}
273 
274 		if (username == null) {
275 			throw new Exception("Username not specified");
276 		}
277 
278 		if("".equals(roles)) {
279 			return;
280 		}
281 
282 		if (!SecureUserStore.existsUser(username)) {
283 			throw new Exception("Such user does not exist");
284 		}
285 
286 		SecureUserStore.addRoles(username, roles);
287 	}
288 
289 	/**
290 	 * Command to remove roles for a particular user
291 	 *
292 	 * @param args command line arguments in the format -username <username>\r\n-roles <comma-separated list of roles to remove>
293 	 * @throws Exception
294 	 */
295 	@Descriptor("Remove user roles")
removeRoles(@escriptorR) String[] args)296 	public void removeRoles(@Descriptor("-username <username>\r\n-roles <comma-separated list of roles to remove>") String[] args) throws Exception {
297 		String username = null;
298 		String roles = "";
299 
300 		for (int i = 0; i < args.length; i++) {
301 			if ("-username".equals(args[i]) && i < args.length - 1) {
302 				username = args[i + 1];
303 				i++;
304 			} else if ("-roles".equals(args[i]) && i < args.length - 1) {
305 				roles = args[i + 1];
306 				i++;
307 			}
308 		}
309 
310 		if (username == null) {
311 			throw new Exception("Username not specified");
312 		}
313 
314 		if("".equals(roles)) {
315 			return;
316 		}
317 
318 		if (!SecureUserStore.existsUser(username)) {
319 			throw new Exception("Such user does not exist");
320 		}
321 
322 		SecureUserStore.removeRoles(username, roles);
323 	}
324 
325 	/**
326 	 * Command to list available users
327 	 */
328 	@Descriptor("Lists available users")
listUsers()329 	public void listUsers() {
330 
331 		String[] users = SecureUserStore.getUserNames();
332 
333 		if(users.length == 0) {
334 			System.out.println("No users available");
335 			return;
336 		}
337 
338 		for(String user : users) {
339 			System.out.println(user);
340 		}
341 	}
342 
readPassword(BufferedReader reader)343 	private String readPassword(BufferedReader reader) {
344 		String password = null;
345 		int count = 0;
346 
347 		while (password == null && count < PASSWORD_INPUT_TRIALS_LIMIT){
348 			System.out.print("password: ");
349 			System.out.flush();
350 
351 			try {
352 				password = reader.readLine();
353 			} catch (IOException e) {
354 				System.out.println("Error while reading password");
355 				return null;
356 			}
357 
358 
359 			if (password == null || "".equals(password)) {
360 				System.out.println("Password not specified");
361 				password = null;
362 			} else if (password.length() < MINIMAL_PASSWORD_LENGTH) {
363 				System.out.println("Password should be at least 8 characters");
364 				password = null;
365 			}
366 
367 			count++;
368 		}
369 
370 		if (password == null) {
371 			return null;
372 		}
373 
374 		String passwordConfirmation = null;
375 		count = 0;
376 
377 		while (passwordConfirmation == null && count < PASSWORD_INPUT_TRIALS_LIMIT){
378 			System.out.print("Confirm password: ");
379 			System.out.flush();
380 
381 			try {
382 				passwordConfirmation = reader.readLine();
383 				if (!password.equals(passwordConfirmation)) {
384 					System.out.println("The passwords do not match!");
385 					passwordConfirmation = null;
386 				}
387 			} catch (IOException e) {
388 				System.out.println("Error while reading password");
389 				return null;
390 			}
391 
392 			count++;
393 		}
394 		if (passwordConfirmation == null){
395 			return null;
396 		}
397 		return password;
398 	}
399 
readUsername(BufferedReader reader)400 	private String readUsername (BufferedReader reader) {
401 		System.out.print("username: ");
402 		System.out.flush();
403 		String username = null;
404 
405 		try {
406 			username = reader.readLine();
407 		} catch (IOException e) {
408 			System.out.println("Error while reading username");
409 			return null;
410 		}
411 
412 		if (username == null || "".equals(username)) {
413 			System.out.println("Username not specified");
414 			return null;
415 		}
416 
417 		return username;
418 	}
419 
readRoles(BufferedReader reader)420 	private String readRoles (BufferedReader reader){
421 		//roles input validation
422 		System.out.print("roles: ");
423 		System.out.flush();
424 		String roles = null;
425 		try {
426 			roles = reader.readLine();
427 		} catch (IOException e) {
428 			System.out.println("Error while reading roles");
429 			return null;
430 		}
431 
432 		if (roles == null) {
433 			roles = "";
434 		}
435 		return roles;
436 	}
437 
validateUsername(String username)438 	private static boolean validateUsername (String username){
439 		if( username == null){
440 			return false;
441 		}else{
442 			Pattern allowedChars = Pattern.compile("[A-Za-z0-9_.]+");
443 			Matcher matcher = allowedChars.matcher(username);
444 			return matcher.matches();
445 		}
446 	}
447 
448 }
449