/*
* Copyright 2015 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*
*
* Copyright (c) 2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*
*/
package io.vertx.ext.shell;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.Session;
import io.vertx.core.AsyncResult;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.mongo.MongoAuth;
import io.vertx.ext.mongo.MongoClient;
import io.vertx.ext.shell.command.CommandBuilder;
import io.vertx.ext.shell.command.CommandRegistry;
import io.vertx.ext.shell.term.SSHTermOptions;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class SSHShellTest extends SSHTestBase {
ShellService service;
@After
public void after() throws Exception {
if (service != null) {
CountDownLatch latch = new CountDownLatch(1);
Handler<AsyncResult<Void>> handler = ar -> latch.countDown();
service.stop(handler);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
super.after();
}
protected void startShell(SSHTermOptions options) throws ExecutionException, InterruptedException, TimeoutException {
if (service != null) {
throw new IllegalStateException();
}
service = ShellService.create(vertx, new ShellServiceOptions().
setWelcomeMessage("").
setSSHOptions(options));
CompletableFuture<Void> fut = new CompletableFuture<>();
service.start(ar -> {
if (ar.succeeded()) {
fut.complete(null);
} else {
fut.completeExceptionally(ar.cause());
}
});
fut.get(10, TimeUnit.SECONDS);
}
@Test
public void testDeployServiceWithShiroAuthOptions(TestContext context) throws Exception {
Async async = context.async();
vertx.deployVerticle("service:io.vertx.ext.shell", new DeploymentOptions().setConfig(new JsonObject().put("sshOptions",
new JsonObject().
put("host", "localhost").
put("port", 5000).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/server-keystore.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().put("provider", "shiro").put("config",
new JsonObject().
put("properties_path", "classpath:test-auth.properties")))))
, context.asyncAssertSuccess(v -> {
async.complete();
}));
async.awaitSuccess(2000);
Session session = createSession("paulo", "secret", false);
session.connect();
Channel channel = session.openChannel("shell");
channel.connect();
channel.disconnect();
session.disconnect();
}
protected static JsonObject config() {
return new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver");
}
@Test
public void testDeployServiceWithJDBCAuthOptions(TestContext context) throws Exception {
List<String> SQL = Arrays.asList(
"create table user (username varchar(255), password varchar(255), password_salt varchar(255) );",
"create table user_roles (username varchar(255), role varchar(255));",
"insert into user values ('tim', 'EC0D6302E35B7E792DF9DA4A5FE0DB3B90FCAB65A6215215771BF96D498A01DA8234769E1CE8269A105E9112F374FDAB2158E7DA58CDC1348A732351C38E12A0', 'C59EB438D1E24CACA2B1A48BC129348589D49303858E493FBE906A9158B7D5DC');"
);
Connection conn = DriverManager.getConnection(config().getString("url"));
for (String sql : SQL) {
conn.createStatement().execute(sql);
}
Async async = context.async();
vertx.deployVerticle("service:io.vertx.ext.shell", new DeploymentOptions().setConfig(new JsonObject().put("sshOptions",
new JsonObject().
put("host", "localhost").
put("port", 5000).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/server-keystore.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().put("provider", "jdbc").put("config",
new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver")))))
, context.asyncAssertSuccess(v -> {
async.complete();
}));
async.awaitSuccess(2000);
Session session = createSession("tim", "sausages", false);
session.connect();
Channel channel = session.openChannel("shell");
channel.connect();
channel.disconnect();
session.disconnect();
}
@Test
public void testDeployServiceWithMongoAuthOptions(TestContext context) throws Exception {
Async mongo = context.async();
vertx.deployVerticle("service:io.vertx.vertx-mongo-embedded-db", context.asyncAssertSuccess(v -> mongo.complete()));
mongo.awaitSuccess(120 * 1000); // Enough time for downloading the db
JsonObject config = new JsonObject().put("connection_string", "mongodb://localhost:27018");
MongoClient client = MongoClient.createNonShared(vertx, config);
MongoAuth auth = MongoAuth.create(client, new JsonObject());
Async ready = context.async();
auth.insertUser("admin", "password", Collections.emptyList(), Collections.emptyList(), context.asyncAssertSuccess(v -> ready.complete()));
ready.awaitSuccess(2000);
Async async = context.async();
vertx.deployVerticle("service:io.vertx.ext.shell", new DeploymentOptions().setConfig(new JsonObject().put("sshOptions",
new JsonObject().
put("host", "localhost").
put("port", 5000).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/server-keystore.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().put("provider", "mongo").put("config",
new JsonObject().
put("connection_string", "mongodb://localhost:27018")))))
, context.asyncAssertSuccess(v -> {
async.complete();
}));
async.awaitSuccess(2000);
Session session = createSession("admin", "password", false);
session.connect();
Channel channel = session.openChannel("shell");
channel.connect();
channel.disconnect();
session.disconnect();
}
@Override
public void testExec(TestContext context) throws Exception {
AtomicReference<Vertx> execCommand = new AtomicReference<>();
CommandRegistry registry = CommandRegistry.getShared(vertx);
registry.registerCommand(CommandBuilder.command("the-command").processHandler(process -> {
context.assertNotNull(Vertx.currentContext());
context.assertNotNull(process.session());
execCommand.set(process.vertx());
context.assertEquals(-1, process.width());
context.assertEquals(-1, process.height());
context.assertEquals(Arrays.asList("arg1", "arg2"), process.args());
context.assertTrue(process.isForeground());
StringBuilder input = new StringBuilder();
process.stdinHandler(data -> {
input.append(data);
if (input.toString().equals("the_input")) {
process.end(2);
}
});
process.write("the_output");
}).build(vertx));
super.testExec(context);
assertEquals(execCommand.get(), vertx);
}
}