Skip to content

Commit

Permalink
IJMP-1814 Fix for connection host/port validation
Browse files Browse the repository at this point in the history
Signed-off-by: Katsiaryna Tsytsenia <[email protected]>
  • Loading branch information
Katsiaryna Tsytsenia authored and Katsiaryna Tsytsenia committed Aug 13, 2024
1 parent 805e9b6 commit 9ec08aa
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 20 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ val junitVersion = "5.10.2"
val mockkVersion = "1.13.10"
val ibmMqVersion = "9.3.5.0"
val jGraphTVersion = "1.5.2"
val zoweKotlinSdkVersion = "0.5.0-rc.9"
val zoweKotlinSdkVersion = "0.5.0-rc.11"
val javaKeytarVersion = "1.0.0"

repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,18 @@ class ZOSMFConnectionConfigurable : BoundSearchableConfigurable("z/OSMF Connecti
showAndTestConnection()?.let { connectionsTableModel?.addRow(it) }
}

/**Unable to save invalid URL
* Updates the selected profilef or current connection.
* If update is not possible(brocken URL), throws IllegalStateException exception
*/
@Throws(IllegalStateException::class)
private fun ZoweConfig.updateFromState(state: ConnectionDialogState) {
setProfile(ZoweConfigServiceImpl.getProfileNameFromConnName(state.connectionName))
val uri = URI(state.connectionUrl)
if (uri.host.isNullOrEmpty())
throw IllegalStateException("Unable to save invalid URL: ${state.connectionUrl}")
setProfile(ZoweConfigServiceImpl.getProfileNameFromConnName(state.connectionName))
host = uri.host
port = uri.port.toLong()
port = if (uri.port==-1) 10443 else uri.port.toLong()
protocol = state.connectionUrl.split("://")[0]
user = state.username
password = state.password
Expand Down Expand Up @@ -100,13 +107,21 @@ class ZOSMFConnectionConfigurable : BoundSearchableConfigurable("z/OSMF Connecti

val zoweConfig = parseConfigJson(configFile.inputStream)
zoweConfig.extractSecureProperties(configFile.path.split("/").toTypedArray())
zoweConfig.updateFromState(state)
runWriteActionOnWriteThread {
zoweConfig.setProfile(ZoweConfigServiceImpl.getProfileNameFromConnName(state.connectionName))
zoweConfig.saveSecureProperties(configFile.path.split("/").toTypedArray())
zoweConfig.restoreProfile()
configFile.setBinaryContent(zoweConfig.toJson().toByteArray(configFile.charset))
kotlin.runCatching {
zoweConfig.updateFromState(state)
}
.onSuccess {
runWriteActionOnWriteThread {
zoweConfig.setProfile(ZoweConfigServiceImpl.getProfileNameFromConnName(state.connectionName))
zoweConfig.saveSecureProperties(configFile.path.split("/").toTypedArray())
zoweConfig.restoreProfile()
configFile.setBinaryContent(zoweConfig.toJson().toByteArray(configFile.charset))
}
}
.onFailure {
Messages.showErrorDialog("Unable to save invalid URL: ${state.connectionUrl}", "Invalid URL")
return
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import org.zowe.explorer.explorer.ui.UssDirNode
import org.zowe.explorer.explorer.ui.UssFileNode
import org.zowe.explorer.utils.crudable.Crudable
import org.zowe.explorer.utils.crudable.find
import org.zowe.kotlinsdk.DatasetOrganization
import javax.swing.JComponent
import javax.swing.JPasswordField
import javax.swing.JTextField

private val urlRegex = Regex("^(https?|http)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]")
private val urlRegex =
Regex("^(https?|http)://[-a-zA-Z0-9+&@#/%?=~_|!,.;]*(:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{1,5})|([0-9]{1,4})))?")
private val maskRegex = Regex("^[A-Za-z\\$\\*%@#][A-Za-z0-9\\-\\$\\*%@#]{0,7}")
private val ussPathRegex = Regex("^/$|^(/[^/]+)+$")
private val forbiddenSymbol = "/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vfs.VirtualFileManager
import org.zowe.explorer.config.ConfigService
import org.zowe.explorer.config.connect.ConnectionConfig
import org.zowe.explorer.config.connect.CredentialService
import org.zowe.explorer.config.connect.getPassword
import org.zowe.explorer.config.connect.getUsername
import org.zowe.explorer.config.connect.*
import org.zowe.explorer.config.connect.ui.zosmf.ConnectionDialogState
import org.zowe.explorer.config.connect.ui.zosmf.ZOSMFConnectionConfigurable.Companion.warningMessageForDeleteConfig
import org.zowe.explorer.config.connect.whoAmI
import org.zowe.explorer.config.ws.FilesWorkingSetConfig
import org.zowe.explorer.config.ws.JesWorkingSetConfig
import org.zowe.explorer.dataops.DataOpsManager
Expand Down Expand Up @@ -391,15 +387,17 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService
createZoweSchemaJsonIfNotExists()

val urlRegex =
"(https?:\\/\\/)(www\\.)?([-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})\b?([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*)"
"^(https?|http)://([-a-zA-Z0-9+&@#/%?=~_|!,.;]*)(:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{1,5})|([0-9]{1,4})))?"
val pattern: Pattern = Pattern.compile(urlRegex)
val matcher: Matcher = pattern.matcher(state.connectionUrl)

var host = "localhost"
var port = "443"
var port = "10443"
if (matcher.matches()) {
host = matcher.group(3)
port = matcher.group(4).substring(1)
if (matcher.group(2) != null)
host = matcher.group(2)
if (matcher.group(3) != null)
port = matcher.group(3).substring(1)
}

val content = getResourceStream("files/${ZOWE_CONFIG_NAME}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package org.zowe.explorer.config.connect.ui.zosmf

import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.showOkCancelDialog
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.mockk.*
import org.zowe.explorer.testutils.WithApplicationShouldSpec
import org.zowe.kotlinsdk.zowe.config.DefaultKeytarWrapper
import org.zowe.kotlinsdk.zowe.config.KeytarWrapper
import org.zowe.kotlinsdk.zowe.config.ZoweConfig
import java.nio.file.Path
import javax.swing.Icon
import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredMemberFunctions
import kotlin.reflect.jvm.isAccessible

class ZOSMFConnectionConfigurableTest : WithApplicationShouldSpec({

val zOSMFConnectionConfigurableMock = spyk<ZOSMFConnectionConfigurable>()
var isShowOkCancelDialogCalled = false
var isFindFileByNioPathCalled = false
var isInputStreamCalled = false

afterSpec {
clearAllMocks()
unmockkAll()
}

beforeEach {
isShowOkCancelDialogCalled = false
isFindFileByNioPathCalled = false
isInputStreamCalled = false
}

context("ZOSMFConnectionConfigurable:") {

val state = ConnectionDialogState(
connectionUuid = "0000",
connectionUrl = "https://111.111.111.111:111",
connectionName = "zowe-local-zosmf/testProj",
zoweConfigPath = "/zowe/conf/path"
)

val ret = mutableListOf<Int>(Messages.OK)
val showOkCancelDialogMock: (String, String, String, String, Icon?, DialogWrapper.DoNotAskOption?, Project?) -> Int =
::showOkCancelDialog
mockkStatic(showOkCancelDialogMock as KFunction<*>)
every {
showOkCancelDialogMock(any<String>(), any<String>(), any<String>(), any<String>(), null, null, null)
} answers {
isShowOkCancelDialogCalled = true
ret[0]
}

mockkConstructor(DefaultKeytarWrapper::class)
every { anyConstructed<DefaultKeytarWrapper>().setPassword(any(), any(), any()) } just Runs
every { anyConstructed<DefaultKeytarWrapper>().deletePassword(any(), any()) } returns true

should("updateZoweConfigIfNeeded null state and Ok") {
zOSMFConnectionConfigurableMock::class.declaredMemberFunctions.find { it.name == "updateZoweConfigIfNeeded" }
?.let {
it.isAccessible = true
try {
it.call(zOSMFConnectionConfigurableMock, null)
} catch (t: Throwable) {
t.cause.toString().shouldContain("Zowe config file not found")
}
}
isShowOkCancelDialogCalled shouldBe true
isFindFileByNioPathCalled shouldBe false
}

should("updateZoweConfigIfNeeded null zoweConfigPath and Ok") {
zOSMFConnectionConfigurableMock::class.declaredMemberFunctions.find { it.name == "updateZoweConfigIfNeeded" }
?.let {
it.isAccessible = true
try {
state.zoweConfigPath = null
it.call(zOSMFConnectionConfigurableMock, state)
} catch (t: Throwable) {
t.cause.toString().shouldContain("Zowe config file not found")
}
}
isShowOkCancelDialogCalled shouldBe true
isFindFileByNioPathCalled shouldBe false
}

ret[0] = Messages.CANCEL

should("updateZoweConfigIfNeeded null state and Cancel") {
zOSMFConnectionConfigurableMock::class.declaredMemberFunctions.find { it.name == "updateZoweConfigIfNeeded" }
?.let {
it.isAccessible = true
try {
state.zoweConfigPath = null
it.call(zOSMFConnectionConfigurableMock, null)
} catch (t: Throwable) {
t.cause.toString().shouldContain("Zowe config file not found")
}
}
isShowOkCancelDialogCalled shouldBe true
isFindFileByNioPathCalled shouldBe false
}

state.zoweConfigPath = "/zowe/conf/path"
ret[0] = Messages.OK

should("updateZoweConfigIfNeeded throw Zowe config file not found") {
zOSMFConnectionConfigurableMock::class.declaredMemberFunctions.find { it.name == "updateZoweConfigIfNeeded" }
?.let {
it.isAccessible = true
try {
it.call(zOSMFConnectionConfigurableMock, state)
} catch (t: Throwable) {
t.cause.toString().shouldContain("Zowe config file not found")
}
}
isShowOkCancelDialogCalled shouldBe true
isFindFileByNioPathCalled shouldBe false
}

val vfMock = mockk<VirtualFile>()
val vfmMock: VirtualFileManager = mockk<VirtualFileManager>()
mockkStatic(VirtualFileManager::class)
every { VirtualFileManager.getInstance() } returns vfmMock
every { vfmMock.findFileByNioPath(any<Path>()) } answers {
isFindFileByNioPathCalled = true
vfMock
}
every { vfMock.inputStream } answers {
isInputStreamCalled = true
val fileCont = "{\n" +
" \"\$schema\": \"./zowe.schema.json\",\n" +
" \"profiles\": {\n" +
" \"zosmf\": {\n" +
" \"type\": \"zosmf\",\n" +
" \"properties\": {\n" +
" \"port\": 443\n" +
" },\n" +
" \"secure\": []\n" +
" },\n" +
" \"tso\": {\n" +
" \"type\": \"tso\",\n" +
" \"properties\": {\n" +
" \"account\": \"\",\n" +
" \"codePage\": \"1047\",\n" +
" \"logonProcedure\": \"IZUFPROC\"\n" +
" },\n" +
" \"secure\": []\n" +
" },\n" +
" \"ssh\": {\n" +
" \"type\": \"ssh\",\n" +
" \"properties\": {\n" +
" \"port\": 22\n" +
" },\n" +
" \"secure\": []\n" +
" },\n" +
" \"base\": {\n" +
" \"type\": \"base\",\n" +
" \"properties\": {\n" +
" \"host\": \"example.host\",\n" +
" \"rejectUnauthorized\": true\n" +
" },\n" +
" \"secure\": [\n" +
" \"user\",\n" +
" \"password\"\n" +
" ]\n" +
" }\n" +
" },\n" +
" \"defaults\": {\n" +
" \"zosmf\": \"zosmf\",\n" +
" \"tso\": \"tso\",\n" +
" \"ssh\": \"ssh\",\n" +
" \"base\": \"base\"\n" +
" }\n" +
"}"
fileCont.toByteArray().inputStream()
}
every { vfMock.path } returns "/zowe/file/path/zowe.config.json"
every { vfMock.charset } returns Charsets.UTF_8
every { vfMock.setBinaryContent(any()) } just Runs

mockkObject(ZoweConfig)
val confMap = mutableMapOf<String, MutableMap<String, String>>()
val configCredentialsMap = mutableMapOf<String, String>()
configCredentialsMap["profiles.base.properties.user"] = "testUser"
configCredentialsMap["profiles.base.properties.password"] = "testPass"
confMap.clear()
confMap["/zowe/file/path/zowe.config.json"] = configCredentialsMap
every { ZoweConfig.Companion["readZoweCredentialsFromStorage"](any<KeytarWrapper>()) } returns confMap

should("updateZoweConfigIfNeeded success") {
zOSMFConnectionConfigurableMock::class.declaredMemberFunctions.find { it.name == "updateZoweConfigIfNeeded" }
?.let {
it.isAccessible = true
it.call(zOSMFConnectionConfigurableMock, state)
isShowOkCancelDialogCalled shouldBe true
isFindFileByNioPathCalled shouldBe true
isInputStreamCalled shouldBe true
}
}

should("updateZoweConfigIfNeeded failed") {
zOSMFConnectionConfigurableMock::class.declaredMemberFunctions.find { it.name == "updateZoweConfigIfNeeded" }
?.let {
it.isAccessible = true
state.connectionUrl = "111@@@:8080"
try {
it.call(zOSMFConnectionConfigurableMock, state)
} catch (t: Throwable) {
t.cause.toString().shouldContain("Unable to save invalid URL")
}
}
}
}
}
)

0 comments on commit 9ec08aa

Please sign in to comment.