Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sonarqube Widget - token authentication support #283

Merged
merged 23 commits into from
Oct 23, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2a7a04e
feature/token-auth-sonarqube - authentication one tab in settings + b…
yahorm Oct 20, 2020
dd6394f
feature/token-auth-sonarqube
yahorm Oct 20, 2020
9558a61
feature/token-auth-sonarqube - small refactoring
yahorm Oct 20, 2020
960b16f
feature/token-auth-sonarqube - remove tab
yahorm Oct 21, 2020
1b5c487
feature/token-auth-sonarqube - cleanup credential form
yahorm Oct 21, 2020
82109e4
Merge branch 'master' into feature/token-auth-sonarqube
yahorm Oct 21, 2020
f02b91c
feature/token-auth-sonarqube - cr refactoring
yahorm Oct 21, 2020
bc46a3d
feature/token-auth-sonarqube - small refactoring
yahorm Oct 21, 2020
328f0b9
feature/token-auth-sonarqube - cr fixes
yahorm Oct 22, 2020
f1bc2ac
feature/token-auth-sonarqube - revert files from master
yahorm Oct 22, 2020
adad2aa
feature/token-auth-sonarqube - fix zabbix widget auth and last value
yahorm Oct 22, 2020
3b95114
feature/token-auth-sonarqube - cypress test fix
yahorm Oct 22, 2020
3a62f8a
feature/token-auth-sonarqube - screenshot for credentials dialog
yahorm Oct 22, 2020
02d1852
feature/token-auth-sonarqube - console error cleanup
yahorm Oct 22, 2020
b134742
Adding icon with info for token field in credential form
Oct 22, 2020
7ca2a94
Merge branch 'feature/token-auth-sonarqube' of https://github.com/Cog…
Oct 22, 2020
f161804
feature/token-auth-sonarqube - remove unused import
yahorm Oct 22, 2020
f84b06d
feature/token-auth-sonarqube - made username, password and token opti…
yahorm Oct 23, 2020
1a9d1a2
feature/token-auth-sonarqube - fix zabbix uptime
yahorm Oct 23, 2020
53badcf
feature/token-auth-sonarqube - remove unused styles
yahorm Oct 23, 2020
700bc1b
feature/token-auth-sonarqube - remove unused styles
yahorm Oct 23, 2020
24d75a9
feature/token-auth-sonarqube - mocks uptime change value
yahorm Oct 23, 2020
5d0f227
Merge branch 'master' into feature/token-auth-sonarqube
szymon-owczarzak Oct 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api-mocks/__files/zabbix/uptime.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"evaltype": "0",
"lastclock": "{{now format='epoch'}}",
"lastns": "521184209",
"lastvalue": "{{randomValue length=9 type='NUMERIC'}}",
"lastvalue": "{{randomValue length=7 type='NUMERIC'}}",
"prevvalue": "23292221"
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CogboardConstants {
const val PROP_HEADERS = "headers"
const val PROP_CONTENT = "content"
const val PROP_WIDGET_TYPE = "type"
const val PROP_AUTHENTICATION_TYPES = "authenticationTypes"
const val PROP_SCHEDULE_PERIOD = "schedulePeriod"
const val PROP_SCHEDULE_PERIOD_DEFAULT = 120L // 120 seconds
const val PROP_SCHEDULE_DELAY_DEFAULT = 10L // 10 seconds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package com.cognifide.cogboard.http
import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.CogboardConstants.Companion.PROP_STATUS_CODE
import com.cognifide.cogboard.CogboardConstants.Companion.PROP_STATUS_MESSAGE
import com.cognifide.cogboard.http.auth.AuthenticationFactory
import com.cognifide.cogboard.http.auth.AuthenticationType
import io.vertx.core.AbstractVerticle
import io.vertx.core.buffer.Buffer
import io.vertx.core.json.DecodeException
import io.vertx.core.json.Json
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.Logger
import io.vertx.core.logging.LoggerFactory
Expand Down Expand Up @@ -88,34 +92,68 @@ class HttpClient : AbstractVerticle() {
val pass = config.getString(CogboardConstants.PROP_PASSWORD) ?: ""
val token = config.getString(CogboardConstants.PROP_TOKEN) ?: ""
val headers = config.getJsonObject(CogboardConstants.PROP_HEADERS)
val authenticationTypes = Json.decodeValue(config.getString(CogboardConstants.PROP_AUTHENTICATION_TYPES))
?: JsonArray()

if (user.isNotBlank() && token.isNotBlank()) {
request.basicAuthentication(user, token)
} else if (user.isNotBlank() && pass.isNotBlank()) {
request.basicAuthentication(user, pass)
}
val authenticationType = getAuthenticationType(authenticationTypes as JsonArray, user, token, pass)

request.authenticate(authenticationType, user, token, pass)

applyRequestHeaders(request, headers)

return request
}

private fun HttpRequest<Buffer>.authenticate(
authType: AuthenticationType,
username: String,
token: String,
pass: String
) {
AuthenticationFactory(username, token, pass, this).create(authType)
}

private fun getAuthenticationType(authenticationTypes: JsonArray, user: String, token: String, pass: String): AuthenticationType {
yahorm marked this conversation as resolved.
Show resolved Hide resolved

return authenticationTypes.stream()
.map { AuthenticationType.valueOf(it.toString()) }
.filter { hasAuthTypeCorrectCredentials(it, user, token, pass) }
.findFirst()
.orElse(AuthenticationType.NONE)
}

private fun hasAuthTypeCorrectCredentials(
authType: AuthenticationType,
username: String,
token: String,
pass: String
): Boolean {
return when {
authType == AuthenticationType.TOKEN && username.isNotBlank() && token.isNotBlank() -> true
authType == AuthenticationType.TOKEN_AS_USERNAME && token.isNotBlank() -> true
else -> authType == AuthenticationType.BASIC && username.isNotBlank() && pass.isNotBlank()
}
}

private fun applyRequestHeaders(request: HttpRequest<Buffer>, headers: JsonObject?) {
request.putHeader(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON)
headers
?.map { Pair(it.key, it.value as String) }
?.forEach { request.putHeader(it.first, it.second) }
}

private fun toJson(response: HttpResponse<Buffer>): JsonObject {
return try {
response.bodyAsJsonObject()
} catch (e: DecodeException) {
try {
JsonObject().put(CogboardConstants.PROP_ARRAY, response.bodyAsJsonArray())
} catch (e: DecodeException) {
JsonObject().put("body", response.bodyAsString())
private fun executeCheckRequest(request: HttpRequest<Buffer>, address: String?, body: JsonObject?) {
request.sendJsonObject(body) {
val result = JsonObject()
if (it.succeeded()) {
result.put(PROP_STATUS_CODE, it.result().statusCode())
result.put(PROP_STATUS_MESSAGE, it.result().statusMessage())
result.put(CogboardConstants.PROP_BODY, it.result().bodyAsString())
} else {
result.put(PROP_STATUS_MESSAGE, "unsuccessful")
LOGGER.error(it.cause()?.message)
}
vertx.eventBus().send(address, result)
}
}

Expand All @@ -138,18 +176,17 @@ class HttpClient : AbstractVerticle() {
}
}

private fun executeCheckRequest(request: HttpRequest<Buffer>, address: String?, body: JsonObject?) {
request.sendJsonObject(body) {
val result = JsonObject()
if (it.succeeded()) {
result.put(PROP_STATUS_CODE, it.result().statusCode())
result.put(PROP_STATUS_MESSAGE, it.result().statusMessage())
result.put(CogboardConstants.PROP_BODY, it.result().bodyAsString())
} else {
result.put(PROP_STATUS_MESSAGE, "unsuccessful")
LOGGER.error(it.cause()?.message)
private fun toJson(response: HttpResponse<Buffer>): JsonObject {
return try {
response.bodyAsJsonObject()
} catch (e: DecodeException) {
try {
JsonObject().put(CogboardConstants.PROP_ARRAY, response.bodyAsJsonArray())
} catch (e: DecodeException) {
JsonObject().put(CogboardConstants.PROP_BODY, response.bodyAsString())
}
vertx.eventBus().send(address, result)
} catch (e: IllegalStateException) {
JsonObject()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.cognifide.cogboard.http.auth

import io.vertx.core.buffer.Buffer
import io.vertx.ext.web.client.HttpRequest

class AuthenticationFactory(
private val user: String,
private val token: String,
private val password: String,
private val request: HttpRequest<Buffer>
) {

fun create(authType: AuthenticationType): HttpRequest<Buffer> {
return when {
AuthenticationType.TOKEN == authType -> token()
AuthenticationType.TOKEN_AS_USERNAME == authType -> tokenAsUsername()
AuthenticationType.BASIC == authType -> basic()
else -> request
}
}

private fun basic(): HttpRequest<Buffer> = request.basicAuthentication(user, password)

private fun token(): HttpRequest<Buffer> = request.basicAuthentication(user, token)

private fun tokenAsUsername(): HttpRequest<Buffer> = request.basicAuthentication(token, "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.cognifide.cogboard.http.auth

enum class AuthenticationType {

yahorm marked this conversation as resolved.
Show resolved Hide resolved
BASIC,
TOKEN,
TOKEN_AS_USERNAME,
NONE;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.cognifide.cogboard.widget

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

/**
Expand Down Expand Up @@ -38,6 +40,14 @@ abstract class AsyncWidget(
return super.stop()
}

/**
* Type of authentication.
* Should be overridden for widgets which use `httpGet(..)` from HttpClient
*/
protected open fun authenticationTypes(): Set<AuthenticationType> {
return setOf(AuthenticationType.BASIC)
yahorm marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Notifies Widget that it is time to update.
* Use `httpGet(..)`, `httpPost(..)` or `httpGetStatus(..)` in order to request new state from 3rd party endpoint.
Expand Down Expand Up @@ -90,5 +100,6 @@ abstract class AsyncWidget(
.put(CogboardConstants.PROP_EVENT_ADDRESS, eventBusAddress)
.put(CogboardConstants.PROP_USER, user)
.put(CogboardConstants.PROP_PASSWORD, password)
.put(CogboardConstants.PROP_AUTHENTICATION_TYPES, Json.encode(authenticationTypes()))
yahorm marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.cognifide.cogboard.widget.type

import com.cognifide.cogboard.config.service.BoardsConfigService
import com.cognifide.cogboard.http.auth.AuthenticationType
import com.cognifide.cogboard.widget.AsyncWidget
import com.cognifide.cogboard.widget.Widget
import io.vertx.core.Vertx
Expand All @@ -15,6 +16,10 @@ class JenkinsJobWidget(

private val path: String = config.getString("path", "")

override fun authenticationTypes(): Set<AuthenticationType> {
return setOf(AuthenticationType.TOKEN, AuthenticationType.BASIC)
}

override fun handleResponse(responseBody: JsonObject) {
if (checkAuthorized(responseBody)) {
val lastBuild = responseBody.getJsonObject("lastBuild")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.cognifide.cogboard.CogboardConstants.Companion.REQUEST_METHOD_DELETE
import com.cognifide.cogboard.CogboardConstants.Companion.REQUEST_METHOD_GET
import com.cognifide.cogboard.CogboardConstants.Companion.REQUEST_METHOD_POST
import com.cognifide.cogboard.CogboardConstants.Companion.REQUEST_METHOD_PUT
import com.cognifide.cogboard.http.auth.AuthenticationType
import com.cognifide.cogboard.widget.AsyncWidget
import com.cognifide.cogboard.widget.Widget
import io.netty.util.internal.StringUtil.EMPTY_STRING
Expand All @@ -29,14 +30,16 @@ class ServiceCheckWidget(vertx: Vertx, config: JsonObject) : AsyncWidget(vertx,
get() = if (publicUrl.isNotBlank()) "$publicUrl${config.getString(PROP_PATH, EMPTY_STRING)}"
else config.getString(PROP_PATH, EMPTY_STRING)

override fun authenticationTypes(): Set<AuthenticationType> {
return setOf(AuthenticationType.TOKEN, AuthenticationType.BASIC)
}

override fun updateState() {
if (urlToCheck.isNotBlank()) {
if (requestMethod == REQUEST_METHOD_GET) {
httpGet(url = urlToCheck)
} else if (requestMethod == REQUEST_METHOD_DELETE) {
httpDelete(url = urlToCheck)
} else {
handlePostPut()
when (requestMethod) {
REQUEST_METHOD_GET -> httpGet(url = urlToCheck)
REQUEST_METHOD_DELETE -> httpDelete(url = urlToCheck)
else -> handlePostPut()
yahorm marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
sendConfigurationError("Public URL or Path is blank")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.cognifide.cogboard.widget.type.sonarqube

import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.http.auth.AuthenticationType
import com.cognifide.cogboard.widget.AsyncWidget
import com.cognifide.cogboard.widget.Widget
import io.vertx.core.Vertx
Expand All @@ -14,6 +15,10 @@ class SonarQubeWidget(vertx: Vertx, config: JsonObject) : AsyncWidget(vertx, con
private val selectedMetrics: JsonArray = config.getJsonArray("selectedMetrics")
private val version: Version = Version.getVersion(config.getString("sonarQubeVersion", ""))

override fun authenticationTypes(): Set<AuthenticationType> {
return setOf(AuthenticationType.TOKEN_AS_USERNAME, AuthenticationType.BASIC)
}

override fun handleResponse(responseBody: JsonObject) {
if (checkAuthorized(responseBody)) {
val data = getData(responseBody)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package com.cognifide.cogboard.widget.type.zabbix

import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.config.service.BoardsConfigService
import com.cognifide.cogboard.http.auth.AuthenticationType
import com.cognifide.cogboard.widget.AsyncWidget
import com.cognifide.cogboard.widget.Widget
import io.vertx.core.Vertx
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.Logger
import io.vertx.core.logging.LoggerFactory
import kotlin.math.roundToLong

class ZabbixWidget(
vertx: Vertx,
Expand All @@ -21,6 +23,10 @@ class ZabbixWidget(
private val maxValue: Int = config.getInteger(MAX_VALUE, 0)
private val range: JsonArray = config.getJsonArray(RANGE, JsonArray())

override fun authenticationTypes(): Set<AuthenticationType> {
return setOf(AuthenticationType.NONE)
}

override fun updateState() {
when {
publicUrl.isBlank() -> sendConfigurationError("Endpoint URL is blank.")
Expand Down Expand Up @@ -87,7 +93,7 @@ class ZabbixWidget(
}

private fun getStatusResponse(lastValue: String): Widget.Status {
val convertedValue = lastValue.toLong()
val convertedValue = lastValue.toFloat().roundToLong()
return when {
metricHasMaxValue() -> status(convertedValue.convertToPercentage(maxValue), range)
metricHasProgress() -> status(convertedValue, range)
Expand Down
Loading