Skip to content

Commit

Permalink
Merge pull request #417 from wttech/feature/376-381-412
Browse files Browse the repository at this point in the history
Features 376 381 412
  • Loading branch information
TheTerabit authored Nov 14, 2021
2 parents 2c433cd + 608d790 commit 6d49c96
Show file tree
Hide file tree
Showing 50 changed files with 867 additions and 236 deletions.
11 changes: 11 additions & 0 deletions api-mocks/__files/logViewer/logs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"logLines": [
"02:49:12 127.0.0.1 GET / 200",
"02:49:35 127.0.0.1 GET /index.html 200",
"03:01:06 127.0.0.1 GET /images/sponsered.gif 304",
"03:52:36 127.0.0.1 GET /search.php 200",
"04:17:03 127.0.0.1 GET /admin/style.css 200",
"05:04:54 127.0.0.1 GET /favicon.ico 404",
"05:38:07 127.0.0.1 GET /js/ads.js 200"
]
}
18 changes: 18 additions & 0 deletions api-mocks/mappings/endpoints-mapping.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"mappings": [
{
"request": {
"method": "GET",
"url": "/log-viewer",
"queryParameters": {
"lines": {
"matches": "^[0-9]+$"
}
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"bodyFileName": "logViewer/logs.json"
}
},
{
"request": {
"method": "GET",
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ allprojects {

tasks {
named("build") {
dependsOn(":cogboard-app:test", ":cogboard-webapp:buildImage")
dependsOn(":cogboard-app:test", ":cogboard-webapp:buildImage", ":ssh:buildImage")
}
register("cypressInit", Exec::class) {
setWorkingDir("./functional/cypress-tests")
Expand Down
1 change: 1 addition & 0 deletions cogboard-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.10.0")
implementation(kotlin("stdlib-jdk8"))
implementation("com.jcraft:jsch:0.1.55")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")

testImplementation("org.assertj:assertj-core:3.12.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ class CogboardConstants {
const val SCHEDULE_DELAY_DEFAULT = 10L // 10 seconds
const val SSH_TIMEOUT = 5000 // 5000ms -> 5s
const val SSH_HOST = "sshAddress"
const val SSH_PORT = "sshPort"
const val SSH_KEY = "sshKey"
const val SSH_KEY_PASSPHRASE = "sshKeyPassphrase"
const val URL = "url"
const val LOG_LINES = "logLines"
const val LOG_FILE_PATH = "logFilePath"

const val LOG_REQUEST_TYPE = "logRequestType"
const val LOG_LINES = "logLinesField"
const val REQUEST_ID = "requestId"
const val PUBLIC_URL = "publicUrl"
const val USER = "user"
Expand Down Expand Up @@ -84,6 +86,13 @@ class CogboardConstants {
}
}

class ConnectionType {
companion object {
const val SSH = "SSH"
const val HTTP = "HTTP"
}
}

class RequestMethod {
companion object {
const val GET = "get"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class EndpointLoader(
this.put(Props.USER, credentials.getString(Props.USER) ?: "")
this.put(Props.PASSWORD, credentials.getString(Props.PASSWORD) ?: "")
this.put(Props.TOKEN, credentials.getString(Props.TOKEN) ?: "")
this.put(Props.SSH_KEY, credentials.getString(Props.SSH_KEY) ?: "")
this.put(Props.SSH_KEY_PASSPHRASE, credentials.getString(Props.SSH_KEY_PASSPHRASE) ?: "")
}
}
return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class CredentialsController : AbstractVerticle() {

private fun JsonObject.filterSensitiveData(): JsonObject {
this.remove(Props.PASSWORD)
this.remove(Props.SSH_KEY)
this.remove(Props.SSH_KEY_PASSPHRASE)
return this
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ data class Credential(
val label: String,
val user: String,
val password: String?,
val token: String?
val token: String?,
val sshKey: String?,
val sshKeyPassphrase: String?
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import io.vertx.core.eventbus.MessageConsumer
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.Logger
import io.vertx.core.logging.LoggerFactory
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import java.io.InputStream

class SSHClient : AbstractVerticle() {
Expand Down Expand Up @@ -41,35 +45,41 @@ class SSHClient : AbstractVerticle() {
}
}

private fun tryToConnect(config: JsonObject) {
val eventBusAddress = config.getString(CogboardConstants.Props.EVENT_ADDRESS)
try {
connect(config)
} catch (e: JSchException) {
LOGGER.error(e.message)
vertx.eventBus().send(eventBusAddress, e)
fun tryToConnect(config: JsonObject) {
LOGGER.info(config)
coroutineScope.launch {
try {
connect(config)
} catch (e: JSchException) {
LOGGER.error(e.message)
val eventBusAddress = config.getString(CogboardConstants.Props.EVENT_ADDRESS)
vertx.eventBus().send(eventBusAddress, e.message)
}
}
}

private fun connect(config: JsonObject) {
private suspend fun connect(config: JsonObject) {
val authData = SSHAuthData(config)
createSSHChannel(authData)
executeCommandAndSendResult(config)
}

private fun createSSHChannel(authData: SSHAuthData) {
private suspend fun createSSHChannel(authData: SSHAuthData) {
with(authData) {
initSSHSession(authData)
if (session.isConnected) {
createChannel(createCommand())
} else {
LOGGER.error("Failed to connect to ${authData.host}")
}
}
}

private fun initSSHSession(authData: SSHAuthData) {
jsch = JSch()
jsch.setKnownHosts("~/.ssh/known_hosts")
val session = SessionStrategyFactory(jsch).create(authData).initSession()
// jsch.setKnownHosts("~/.ssh/known_hosts") for security reasons this should be used
session = SessionStrategyFactory(jsch).create(authData).initSession()
session.setConfig("StrictHostKeyChecking", "no") // not secure
session.connect(CogboardConstants.Props.SSH_TIMEOUT)
}

Expand All @@ -83,14 +93,27 @@ class SSHClient : AbstractVerticle() {

private fun executeCommandAndSendResult(config: JsonObject) {
val eventBusAddress = config.getString(CogboardConstants.Props.EVENT_ADDRESS)
val responseBuffer = Buffer.buffer()
responseBuffer.appendBytes(sshInputStream.readAllBytes())
val responseBuffer = readResponse()
vertx.eventBus().send(eventBusAddress, responseBuffer)
channel.disconnect()
session.disconnect()
}

private fun readResponse(): Buffer {
val responseBuffer = Buffer.buffer()
val tmpBuf = ByteArray(512)
var readBytes = sshInputStream.read(tmpBuf, 0, 512)
while (readBytes != -1) {
responseBuffer.appendBytes(tmpBuf, 0, readBytes)
readBytes = sshInputStream.read(tmpBuf, 0, 512)
}

return responseBuffer
}

companion object {
val LOGGER: Logger = LoggerFactory.getLogger(SSHClient::class.java)

val coroutineScope = CoroutineScope(Job() + Dispatchers.IO)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ package com.cognifide.cogboard.ssh.auth

enum class AuthenticationType {
BASIC,
TOKEN,
SSH_KEY
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.cognifide.cogboard.ssh.auth

import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.ssh.auth.AuthenticationType.BASIC
import com.cognifide.cogboard.ssh.auth.AuthenticationType.TOKEN
import com.cognifide.cogboard.ssh.auth.AuthenticationType.SSH_KEY
import io.vertx.core.json.Json
import io.vertx.core.json.JsonArray
Expand All @@ -14,6 +13,7 @@ class SSHAuthData(private val config: JsonObject) {
val token = config.getString(CogboardConstants.Props.TOKEN) ?: ""
val key = config.getString(CogboardConstants.Props.SSH_KEY) ?: ""
val host = config.getString(CogboardConstants.Props.SSH_HOST) ?: ""
val port = config.getInteger(CogboardConstants.Props.SSH_PORT) ?: 22
val authenticationType = fromConfigAuthenticationType()

private fun fromConfigAuthenticationType(): AuthenticationType {
Expand All @@ -28,21 +28,19 @@ class SSHAuthData(private val config: JsonObject) {

private fun hasAuthTypeCorrectCredentials(authType: AuthenticationType): Boolean =
when {
authType == TOKEN && user.isNotBlank() && token.isNotBlank() -> true
authType == SSH_KEY && key.isNotBlank() -> true
else -> authType == BASIC && user.isNotBlank() && password.isNotBlank()
}

fun getAuthenticationString(): String =
when (authenticationType) {
BASIC -> config.getString(CogboardConstants.Props.PASSWORD)
TOKEN -> config.getString(CogboardConstants.Props.TOKEN)
SSH_KEY -> config.getString(CogboardConstants.Props.SSH_KEY)
}

fun createCommand(): String {
val logLines = config.getString(CogboardConstants.Props.LOG_LINES) ?: "0"
val logFilePath = config.getString(CogboardConstants.Props.LOG_FILE_PATH) ?: ""
val logLines = config.getInteger(CogboardConstants.Props.LOG_LINES) ?: 0
val logFilePath = config.getString(CogboardConstants.Props.PATH) ?: ""

return "cat $logFilePath | tail -$logLines"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.cognifide.cogboard.ssh.session

import com.cognifide.cogboard.ssh.auth.AuthenticationType.BASIC
import com.cognifide.cogboard.ssh.auth.AuthenticationType.TOKEN
import com.cognifide.cogboard.ssh.auth.AuthenticationType.SSH_KEY
import com.cognifide.cogboard.ssh.auth.SSHAuthData
import com.cognifide.cogboard.ssh.session.strategy.BasicAuthSessionStrategy
Expand All @@ -12,7 +11,7 @@ import com.jcraft.jsch.JSch
class SessionStrategyFactory(private val jsch: JSch) {
fun create(authData: SSHAuthData): SessionStrategy =
when (authData.authenticationType) {
BASIC, TOKEN -> {
BASIC -> {
BasicAuthSessionStrategy(jsch, authData)
}
SSH_KEY -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.jcraft.jsch.Session
class BasicAuthSessionStrategy(jsch: JSch, authData: SSHAuthData) : SessionStrategy(jsch, authData) {

override fun initSession(): Session {
val session = jsch.getSession(authData.user, authData.host)
val session = jsch.getSession(authData.user, authData.host, authData.port)
session.setPassword(securityString)

return session
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class SSHKeyAuthSessionStrategy(jSch: JSch, authData: SSHAuthData) : SessionStra
} else {
jsch.addIdentity(securityString, authData.password)
}
val session = jsch.getSession(authData.user, authData.host)
val session = jsch.getSession(authData.user, authData.host, authData.port)
session.setConfig("PreferredAuthentications", "publickey")

return session
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.cognifide.cogboard.widget.type.WorldClockWidget
import com.cognifide.cogboard.widget.type.randompicker.RandomPickerWidget
import com.cognifide.cogboard.widget.type.sonarqube.SonarQubeWidget
import com.cognifide.cogboard.widget.type.zabbix.ZabbixWidget
import com.cognifide.cogboard.widget.type.LogViewerWidget
import com.cognifide.cogboard.widget.type.logviewer.LogViewerWidget
import io.vertx.core.Vertx
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.cognifide.cogboard.widget.connectionStrategy

import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.http.auth.AuthenticationType
import io.vertx.core.Vertx
import io.vertx.core.eventbus.MessageConsumer
import io.vertx.core.json.JsonObject

abstract class ConnectionStrategy(protected val vertx: Vertx, protected val eventBusAddress: String) {
protected fun JsonObject.endpointProp(prop: String): String {
return this.getJsonObject(CogboardConstants.Props.ENDPOINT_LOADED)?.getString(prop) ?: ""
}

protected open fun authenticationTypes(): Set<AuthenticationType> {
return setOf(AuthenticationType.BASIC)
}

abstract fun sendRequest(address: String, arguments: JsonObject)

abstract fun getConsumer(eventBusAddress: String): MessageConsumer<*>

abstract fun handleResponse(response: Any): String
}
Loading

0 comments on commit 6d49c96

Please sign in to comment.