import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public void initWithDefaultUsers(String domainID) throws IDMStoreException {
String newDomainID = initDomainAndRolesWithoutUsers(domainID);
if (newDomainID != null) {
- List<String> userAndAdminRoleIDs = getRoleIDs(newDomainID, Arrays.asList("user", "admin"));
- createUser(newDomainID, "admin", "admin", userAndAdminRoleIDs);
-
- List<String> userRoleID = getRoleIDs(newDomainID, Collections.singletonList("user"));
- createUser(newDomainID, "user", "user", userRoleID);
+ createUser(newDomainID, "admin", "admin", true);
+ createUser(newDomainID, "user", "user", false);
}
}
return newUserID;
}
- private void createGrant(String domainID, String userID, String roleID)
+ public String createUser(String domainID, String userName, String password, boolean isAdmin)
throws IDMStoreException {
+ List<String> roleIDs;
+ if (isAdmin) {
+ roleIDs = getRoleIDs(domainID, Arrays.asList("user", "admin"));
+ } else {
+ roleIDs = getRoleIDs(domainID, Arrays.asList("user"));
+ }
+ return createUser(domainID, userName, password, roleIDs);
+ }
+
+ private void createGrant(String domainID, String userID, String roleID) throws IDMStoreException {
Grant grant = new Grant();
grant.setDomainid(domainID);
grant.setUserid(userID);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+
+This program and the accompanying materials are made available under the
+terms of the Eclipse Public License v1.0 which accompanies this distribution,
+and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-parent</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ <relativePath>../parent</relativePath>
+ </parent>
+
+ <artifactId>aaa-cli-jar</artifactId>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.sf.jopt-simple</groupId>
+ <artifactId>jopt-simple</artifactId>
+ <version>5.0.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-h2-store</artifactId>
+ <exclusions>
+ <exclusion>
+ <!-- Completely disable transitive dependencies -->
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <!-- Now repeat the few really needed dependencies which we would normally get transitively -->
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-authn-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+
+ <!-- Now for the FAT JAR we need to fix up some <scope>provided to be <scope>compile -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>compile</scope> <!-- Not provided -->
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>compile</scope> <!-- Not test -->
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.truth</groupId>
+ <artifactId>truth</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>testutils</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <!-- TODO Remove when https://git.opendaylight.org/gerrit/#/c/48400/ is merged -->
+ <executions>
+ <execution>
+ <id>integration-test</id>
+ <goals>
+ <goal>integration-test</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>verify</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <!-- TODO Use maven-shade-plugin with its interesting minimizeJar property... -->
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <archive>
+ <manifest>
+ <mainClass>org.opendaylight.aaa.cli.jar.Main</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+ </configuration>
+ </plugin>
+<!-- TODO
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <configuration>
+ <failOnError>true</failOnError>
+ </configuration>
+ </plugin>
+ -->
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import static java.util.Arrays.asList;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.opendaylight.aaa.api.IDMStoreException;
+
+/**
+ * Class with main() method and argument parsing etc.
+ * This class ONLY deals with argument parsing etc. and doesn't "do" anything,
+ * yet; this is intentional, and best for true unit test-ability of this class.
+ *
+ * @author Michael Vorburger
+ */
+@SuppressWarnings("checkstyle:RegexpSingleLineJava") // allow System.out / System.err here..
+public abstract class AbstractMain {
+
+ private static final String OPTION_HELP = "h";
+ private static final String OPTION_DB_DIR = "dbd";
+ private static final String OPTION_LIST_USERS = "l";
+ private static final String OPTION_CHANGE_USER = "cu";
+ private static final String OPTION_NEW_USER = "nu";
+ private static final String OPTION_ADMINS = "a";
+ private static final String OPTION_PASS = "p";
+ private static final String OPTION_DEBUG = "X";
+
+ private static final int RETURN_NOT_ENOUGH_ARGS = -1;
+ private static final int RETURN_ABORT_DUE_TO_EXCEPTION = -2;
+ private static final int RETURN_ARGUMENTS_MISMATCHED = -3;
+ protected static final int RETURN_ILLEGAL_ARGUMENTS = -4;
+ private static final int RETURN_ARGUMENTS_INCOMPATIBLE = -5;
+ private static final int RETURN_ARGUMENTS_MISSING = -6;
+
+ @SuppressWarnings({ "unchecked", "checkstyle:IllegalThrows", "checkstyle:IllegalCatch" })
+ public int parseArguments(String[] args) throws Exception {
+ boolean isInDebugLogging = false;
+ try {
+ OptionParser optionParser = getOptionParser();
+ OptionSet optionSet = optionParser.parse(args);
+ if (optionSet.has(OPTION_DEBUG)) {
+ isInDebugLogging = true;
+ }
+ if (!optionSet.nonOptionArguments().isEmpty()) {
+ unrecognizedOptions(optionSet.nonOptionArguments());
+ }
+ if (args.length == 0 || optionSet.has(OPTION_HELP) || !optionSet.nonOptionArguments().isEmpty()) {
+ printHelp(optionParser);
+ return RETURN_NOT_ENOUGH_ARGS;
+ }
+
+ if (optionSet.has(OPTION_CHANGE_USER) && optionSet.has(OPTION_NEW_USER)) {
+ System.err.println("Can't use these options together: -" + OPTION_CHANGE_USER
+ + ", -" + OPTION_NEW_USER);
+ return RETURN_ARGUMENTS_INCOMPATIBLE;
+ } else if (optionSet.has(OPTION_PASS) && !optionSet.has(OPTION_CHANGE_USER)
+ && !optionSet.has(OPTION_NEW_USER)) {
+ System.err.println("If passwords are specificied, then must use one or the other of these options: -"
+ + OPTION_CHANGE_USER + ", -" + OPTION_NEW_USER);
+ return RETURN_ARGUMENTS_MISSING;
+ }
+
+ List<String> userNames;
+ if (optionSet.has(OPTION_CHANGE_USER)) {
+ userNames = (List<String>) optionSet.valuesOf(OPTION_CHANGE_USER);
+ } else { // optionSet.has(OPTION_NEW_USER))
+ userNames = (List<String>) optionSet.valuesOf(OPTION_NEW_USER);
+ }
+ List<String> passwords = (List<String>) optionSet.valuesOf(OPTION_PASS);
+ if (passwords.size() != userNames.size()) {
+ System.err.println("Must give as many user names as passwords");
+ return RETURN_ARGUMENTS_MISMATCHED;
+ }
+
+ File dbDirectory = (File) optionSet.valueOf(OPTION_DB_DIR);
+ setDbDirectory(dbDirectory);
+
+ if (optionSet.has(OPTION_LIST_USERS)) {
+ listUsers();
+ }
+
+ if (optionSet.has(OPTION_CHANGE_USER)) {
+ return resetPasswords(userNames, passwords);
+ } else { // optionSet.has(OPTION_NEW_USER))
+ boolean areAdmins = optionSet.has(OPTION_ADMINS);
+ return addNewUsers(userNames, passwords, areAdmins);
+ }
+
+ } catch (Throwable t) {
+ if (!isInDebugLogging) {
+ System.err.println("Aborting due to " + t.getClass().getSimpleName()
+ + " (use -X to see full stack trace): " + t.getMessage());
+ return RETURN_ABORT_DUE_TO_EXCEPTION;
+ } else {
+ // Java will print the full stack trace if we rethrow it
+ throw t;
+ }
+ }
+ }
+
+ private OptionParser getOptionParser() {
+ return new OptionParser() { {
+ acceptsAll(asList(OPTION_HELP, "?" ), "Show help").forHelp();
+ accepts(OPTION_DB_DIR, "databaseDirectory").withRequiredArg().ofType(File.class)
+ .defaultsTo(new File(".")).describedAs("path");
+ acceptsAll(asList(OPTION_LIST_USERS, "listUsers"), "List all existing users");
+ acceptsAll(asList(OPTION_NEW_USER, "newUser"), "New user to create").withRequiredArg();
+ acceptsAll(asList(OPTION_CHANGE_USER, "changeUser"), "Existing user name to change password")
+ .withRequiredArg();
+ acceptsAll(asList(OPTION_PASS, "passwd"), "New password").withRequiredArg();
+ accepts(OPTION_ADMINS, "New User(s) added with 'admin' role");
+ // TODO accepts("v", "Display version information").forHelp();
+ acceptsAll(asList(OPTION_DEBUG, "debug"), "Produce execution debug output");
+
+ allowsUnrecognizedOptions();
+ }
+ };
+ }
+
+ protected void unrecognizedOptions(List<?> unrecognizedOptions) {
+ System.err.println("Unrecognized options: " + unrecognizedOptions);
+ }
+
+ protected void printHelp(OptionParser optionParser) throws IOException {
+ optionParser.printHelpOn(System.out);
+ }
+
+ // ----
+
+ protected abstract void setDbDirectory(File dbDirectory) throws IOException, IDMStoreException;
+
+ protected abstract void listUsers() throws IDMStoreException;
+
+ protected abstract int resetPasswords(List<String> userNames, List<String> passwords) throws IDMStoreException;
+
+ protected abstract int addNewUsers(List<String> userNames, List<String> passwords, boolean areAdmins) throws IDMStoreException;
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.opendaylight.aaa.api.IDMStoreException;
+
+/**
+ * TestableMain which actually does something real.
+ *
+ * @author Michael Vorburger
+ */
+@SuppressWarnings("checkstyle:RegexpSingleLineJava") // allow System.out / System.err here..
+public class Main extends AbstractMain {
+
+ private StandaloneCommandLineInterface cli;
+
+ @SuppressWarnings("checkstyle:IllegalThrows")
+ public static void main(String[] args) throws Exception {
+ System.exit(new Main().parseArguments(args));
+ }
+
+ @Override
+ protected void setDbDirectory(File dbDirectory) throws IOException, IDMStoreException {
+ cli = new StandaloneCommandLineInterface(dbDirectory);
+ }
+
+ @Override
+ protected void listUsers() throws IDMStoreException {
+ System.out.println("User names:");
+ List<String> userNames = cli.getAllUserNames();
+ for (String userName : userNames) {
+ System.out.println(userName);
+ }
+ }
+
+ @Override
+ protected int resetPasswords(List<String> userNames, List<String> passwords) throws IDMStoreException {
+ for (int i = 0; i < userNames.size(); i++) {
+ String userName = userNames.get(i);
+ String newPassword = passwords.get(i);
+ boolean isSuccess = cli.resetPassword(userName, newPassword);
+ if (isSuccess) {
+ // Output text shamelessly copy/pasted from org.opendaylight.aaa.cli.ChangeUserPassword
+ System.out.println(userName + "'s password has been changed");
+ } else {
+ System.err.println("User does not exist: " + userName);
+ return RETURN_ILLEGAL_ARGUMENTS;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ protected int addNewUsers(List<String> userNames, List<String> passwords, boolean areAdmins) throws IDMStoreException {
+ for (int i = 0; i < userNames.size(); i++) {
+ String userName = userNames.get(i);
+ String newPassword = passwords.get(i);
+ cli.createNewUser(userName, newPassword, areAdmins);
+ System.out.print("New user created");
+ if (areAdmins){
+ System.out.print(", as admin");
+ }
+ System.out.println(": " + userName);
+ }
+ return 0;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import com.google.common.base.Preconditions;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.opendaylight.aaa.api.IDMStoreException;
+import org.opendaylight.aaa.api.IIDMStore;
+import org.opendaylight.aaa.api.StoreBuilder;
+import org.opendaylight.aaa.api.model.User;
+import org.opendaylight.aaa.h2.config.IdmLightConfig;
+import org.opendaylight.aaa.h2.config.IdmLightConfigBuilder;
+import org.opendaylight.aaa.h2.config.IdmLightSimpleConnectionProvider;
+import org.opendaylight.aaa.h2.persistence.H2Store;
+
+/**
+ * AAA CLI interface.
+ * This is for a "standalone Java" environment (i.e. plain JSE; non-OSGi, no Karaf).
+ *
+ * @author Michael Vorburger
+ */
+public class StandaloneCommandLineInterface {
+
+ private final IIDMStore identityStore;
+ private final StoreBuilder storeBuilder;
+ private static final String DOMAIN = IIDMStore.DEFAULT_DOMAIN;
+
+ public StandaloneCommandLineInterface(File directoryWithDatabaseFile) throws IOException, IDMStoreException {
+ IdmLightConfigBuilder configBuider = new IdmLightConfigBuilder();
+ configBuider.dbDirectory(directoryWithDatabaseFile.getCanonicalPath());
+ IdmLightConfig config = configBuider.build();
+
+ H2Store h2Store = new H2Store(new IdmLightSimpleConnectionProvider(config));
+ this.identityStore = h2Store;
+
+ this.storeBuilder = new StoreBuilder(h2Store);
+ storeBuilder.initDomainAndRolesWithoutUsers(DOMAIN);
+ }
+
+ public List<String> getAllUserNames() throws IDMStoreException {
+ List<User> users = identityStore.getUsers().getUsers();
+ return users.stream().map(user -> user.getName()).collect(Collectors.toList());
+ }
+
+ public boolean resetPassword(String userIdWithoutDomain, String newPassword) throws IDMStoreException {
+ Preconditions.checkNotNull(userIdWithoutDomain, "userIdWithoutDomain == null");
+ List<User> users = identityStore.getUsers(userIdWithoutDomain, DOMAIN).getUsers();
+ if (users.isEmpty()) {
+ return false;
+ }
+ if (users.size() > 1) {
+ throw new IDMStoreException("More than 1 user found: " + userIdWithoutDomain);
+ }
+ User user = users.get(0);
+ user.setPassword(newPassword);
+ identityStore.updateUser(user);
+ return true;
+ }
+
+ public void createNewUser(String userName, String password, boolean isAdmin) throws IDMStoreException {
+ Preconditions.checkNotNull(userName, "userName == null");
+ storeBuilder.createUser(DOMAIN, userName, password, isAdmin);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.yangtools.testutils.mockito.MoreAnswers;
+
+/**
+ * Unit Test of Main class with the argument parsing.
+ *
+ * @author Michael Vorburger
+ */
+public class AbstractMainTest {
+
+ private AbstractMain mockedMain() {
+ return Mockito.mock(AbstractMain.class, MoreAnswers.realOrException());
+ }
+
+ @Test
+ public void noArguments() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] {})).isEqualTo(-1);
+ Mockito.verify(main).printHelp(any());
+ }
+
+ @Test
+ public void unrecognizedArgument() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] { "saywhat" })).isEqualTo(-1);
+ Mockito.verify(main).unrecognizedOptions(Collections.singletonList("saywhat"));
+ Mockito.verify(main).printHelp(any());
+ }
+
+ /**
+ * Verify that allowsUnrecognizedOptions() is used, and "bad" arguments
+ * print help message instead of causing an UnrecognizedOptionException.
+ */
+ @Test
+ public void parsingError() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] { "-d" })).isEqualTo(-1);
+ Mockito.verify(main).printHelp(any());
+ }
+
+ @Test
+ public void exceptionWithoutX() throws Exception {
+ AbstractMain main = mockedMain();
+ Mockito.doThrow(new IllegalStateException()).when(main).printHelp(any());
+ assertThat(main.parseArguments(new String[] { "-?" })).isEqualTo(-2);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void exceptionWithX() throws Exception {
+ AbstractMain main = mockedMain();
+ Mockito.doThrow(new IllegalStateException()).when(main).printHelp(any());
+ assertThat(main.parseArguments(new String[] { "-hX" })).isEqualTo(-2);
+ }
+
+ @Test
+ public void onlyAUser() throws Exception {
+ assertThat(mockedMain().parseArguments(new String[] { "-X", "--nu", "admin" })).isEqualTo(-3);
+ }
+
+ @Test
+ public void onlyTwoUsers() throws Exception {
+ assertThat(mockedMain().parseArguments(new String[] { "-X", "-cu", "admin", "-cu", "auser" }))
+ .isEqualTo(-3);
+ }
+
+ @Test
+ public void userOptionWithoutArgument() throws Exception {
+ assertThat(mockedMain().parseArguments(new String[] { "-nu" })).isEqualTo(-2);
+ }
+
+ @Test
+ public void ifPasswordsThenEitherCreateOrChangeUser() throws Exception {
+ assertThat(mockedMain().parseArguments(new String[] { "-X", "-p", "newpass" })).isEqualTo(-6);
+ }
+
+ @Test
+ public void changeUserAndPassword() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] { "-X", "-cu", "user", "-p", "newpass" })).isEqualTo(0);
+ Mockito.verify(main).setDbDirectory(new File("."));
+ Mockito.verify(main).resetPasswords(Collections.singletonList("user"), Collections.singletonList("newpass"));
+ }
+
+ @Test
+ public void addNewUser() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] { "-X", "-nu", "user", "-p", "newpass" })).isEqualTo(0);
+ Mockito.verify(main).setDbDirectory(new File("."));
+ Mockito.verify(main).addNewUsers(Collections.singletonList("user"), Collections.singletonList("newpass"), false);
+ }
+
+ @Test
+ public void addNewAdminUser() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] { "-X", "-nu", "user", "-p", "newpass", "-a" })).isEqualTo(0);
+ Mockito.verify(main).setDbDirectory(new File("."));
+ Mockito.verify(main).addNewUsers(Collections.singletonList("user"), Collections.singletonList("newpass"), true);
+ }
+
+ @Test
+ public void changeUserAndPasswordInNonDefaultDatabase() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(new String[] { "-X", "--dbd", "altDbDir", "-cu", "user", "-p", "newpass" }))
+ .isEqualTo(0);
+ Mockito.verify(main).setDbDirectory(new File("altDbDir"));
+ Mockito.verify(main).resetPasswords(Collections.singletonList("user"), Collections.singletonList("newpass"));
+ }
+
+ @Test
+ public void changeTwoUsersAndPasswords() throws Exception {
+ AbstractMain main = Mockito.spy(AbstractMain.class);
+ assertThat(main.parseArguments(
+ new String[] { "-X", "-cu", "user1", "-p", "newpass1", "-cu", "user2", "-p", "newpass2" }))
+ .isEqualTo(0);
+ Mockito.verify(main).resetPasswords(Arrays.asList("user1", "user2"), Arrays.asList("newpass1", "newpass2"));
+ }
+
+ @Test
+ public void morePasswordsThanUsers() throws Exception {
+ assertThat(mockedMain().parseArguments(new String[] { "-X", "-cu", "admin", "-p", "newpass1", "-cu", "auser",
+ "-p", "newpass2", "-p", "newpass3" })).isEqualTo(-3);
+ }
+
+ @Test
+ public void cantAddAndChangeUsersTogether() throws Exception {
+ assertThat(
+ mockedMain().parseArguments(new String[] { "-X", "-cu", "admin", "-nu", "admin2", "-p", "newpass1" }))
+ .isEqualTo(-5);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * Utilities for Files.
+ *
+ * @author Michael Vorburger
+ */
+public final class FilesUtils {
+ private FilesUtils() {
+ }
+
+ public static void delete(String directory) throws IOException {
+ Path path = Paths.get(directory);
+ if (!path.toFile().exists()) {
+ return;
+ }
+ Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ file.toFile().delete();
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ dir.toFile().delete();
+ if (exc != null) {
+ throw exc;
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.io.File;
+import org.junit.Test;
+
+/**
+ * Integration Test for the built JAR file.
+ *
+ * <p>Note that the maven-failsafe-plugin, not the usual maven-surefire-plugin (for
+ * *Test), runs this *IT AFTER the final "fat" self-executable JAR has been
+ * built.
+ *
+ * @author Michael Vorburger
+ */
+public class MainIT {
+
+ private static final String DIR = "target/" + MainIT.class.getSimpleName();
+
+ @Test
+ public void integrationTestBuildJAR() throws Exception {
+ FilesUtils.delete(DIR);
+
+ // If Output piping to LOG instead of inheritIO() etc. is needed, then
+ // consider using https://github.com/vorburger/MariaDB4j/tree/master/mariaDB4j-core/src/main/java/ch/vorburger/exec
+ Process process = new ProcessBuilder(
+ findJava().getAbsolutePath(),
+ "-jar",
+ findExecutableFatJAR().getAbsolutePath(),
+ "--dbd",
+ DIR,
+ "-a",
+ "--nu",
+ "vorburger",
+ "-p",
+ "nosecret" )
+ .inheritIO()
+ // NO .redirectErrorStream(true)
+ .start();
+ process.waitFor();
+ assertThat(process.exitValue()).isEqualTo(0);
+ }
+
+ private File findExecutableFatJAR() {
+ File targetDirectory = new File(".", "target");
+ File[] jarFiles = targetDirectory.listFiles((dir, name) -> name.endsWith("jar-with-dependencies.jar"));
+ assertThat(jarFiles).named("*jar-with-dependencies.jar files in " + targetDirectory).isNotNull();
+ assertThat(jarFiles).named("*jar-with-dependencies.jar files in " + targetDirectory).hasLength(1);
+ return jarFiles[0];
+ }
+
+ private File findJava() {
+ File javaHome = new File(System.getProperty("java.home"));
+ File javaHomeBin = new File(javaHome, "bin");
+ File javaExecutable = new File(javaHomeBin, "java");
+ return javaExecutable;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+/**
+ * Test of RealMain (and its dependencies; incl. args parsing, real DB, etc.).
+ * This intentionally only tests a very basic scenario end-to-end; more fine grained cases are covered in the
+ * {@link StandaloneCommandLineInterfaceTest} and the {@link AbstractMainTest}.
+ *
+ * @author Michael Vorburger
+ */
+public class MainIntegrationTest {
+
+ private static final String DIR = "target/" + MainIntegrationTest.class.getSimpleName();
+
+ @Test
+ public void testCLI() throws Exception {
+ FilesUtils.delete(DIR);
+ assertThat(new Main()
+ .parseArguments(new String[] { "-X", "--dbd", DIR, "-a", "-nu", "newuser", "-p", "firstpass" }))
+ .isEqualTo(0);
+ assertThat(new Main()
+ .parseArguments(new String[] { "-X", "--dbd", DIR, "-cu", "newuser", "-p", "newpass" }))
+ .isEqualTo(0);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.cli.jar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.io.File;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test of StandaloneCommandLineInterface (and its dependencies; incl. real DB).
+ *
+ * @author Michael Vorburger
+ */
+public class StandaloneCommandLineInterfaceTest {
+
+ private static final String DIR = "target/" + StandaloneCommandLineInterfaceTest.class.getSimpleName();
+
+ StandaloneCommandLineInterface cli;
+
+ @Before
+ public void before() throws Exception {
+ FilesUtils.delete(DIR);
+ cli = new StandaloneCommandLineInterface(new File(DIR));
+ }
+
+ @Test
+ public void testInitialEmptyDatabase() throws Exception {
+ assertThat(cli.getAllUserNames()).isEmpty();
+ }
+
+ @Test
+ public void testCreateNewUserAndSetPassword() throws Exception {
+ cli.createNewUser("test", "testpassword", false);
+ assertThat(cli.getAllUserNames()).hasSize(1);
+ assertThat(cli.getAllUserNames().get(0)).isEqualTo("test");
+
+ assertThat(cli.resetPassword("test", "anothertestpassword")).isTrue();
+ }
+
+ @Test
+ public void testSetPasswordOnNonExistingUser() throws Exception {
+ assertThat(cli.resetPassword("noSuchUID", "...")).isFalse();
+ }
+
+}
<module>aaa-h2-store</module>
<module>aaa-cert</module>
<module>aaa-cli</module>
+ <module>aaa-cli-jar</module>
<module>aaa-filterchain</module>
<module>aaa-cassandra-store</module>
<module>artifacts</module>
<tag>HEAD</tag>
<url>https://wiki.opendaylight.org/view/AAA:Main</url>
</scm>
-</project>
\ No newline at end of file
+</project>