mirror of
https://github.com/corda/corda.git
synced 2025-01-01 02:36:44 +00:00
Merge remote-tracking branch 'open/master' into shams-os-merge-040118
# Conflicts: # node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt # node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt
This commit is contained in:
commit
74c2eb8a0a
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
@ -12,6 +12,7 @@ import net.corda.finance.flows.CashIssueFlow
|
|||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.config.AuthDataSourceType
|
import net.corda.node.services.config.AuthDataSourceType
|
||||||
|
import net.corda.node.services.Permissions
|
||||||
import net.corda.node.services.config.PasswordEncryption
|
import net.corda.node.services.config.PasswordEncryption
|
||||||
import net.corda.node.services.config.SecurityConfiguration
|
import net.corda.node.services.config.SecurityConfiguration
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
@ -21,23 +22,89 @@ import net.corda.testing.internal.IntegrationTestSchemas
|
|||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import net.corda.testing.internal.toDatabaseSchemaName
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||||
|
import org.apache.shiro.authc.credential.DefaultPasswordService
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.Parameterized
|
||||||
import java.sql.DriverManager
|
import java.sql.DriverManager
|
||||||
import java.sql.Statement
|
import java.sql.Statement
|
||||||
import java.util.*
|
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
abstract class UserAuthServiceTest : NodeBasedTest() {
|
/*
|
||||||
|
* Starts Node's instance configured to load clients credentials and permissions from an external DB, then
|
||||||
|
* check authentication/authorization of RPC connections.
|
||||||
|
*/
|
||||||
|
@RunWith(Parameterized::class)
|
||||||
|
class AuthDBTests : NodeBasedTest() {
|
||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
||||||
}
|
}
|
||||||
|
|
||||||
protected lateinit var node: StartedNode<Node>
|
private lateinit var node: StartedNode<Node>
|
||||||
protected lateinit var client: CordaRPCClient
|
private lateinit var client: CordaRPCClient
|
||||||
|
private lateinit var db: UsersDB
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val cacheExpireAfterSecs: Long = 1
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Parameterized.Parameters(name = "password encryption format = {0}")
|
||||||
|
fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameterized.Parameter
|
||||||
|
lateinit var passwordEncryption: PasswordEncryption
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
db = UsersDB(
|
||||||
|
name = "SecurityDataSourceTestDB",
|
||||||
|
users = listOf(UserAndRoles(username = "user",
|
||||||
|
password = encodePassword("foo", passwordEncryption),
|
||||||
|
roles = listOf("default"))),
|
||||||
|
roleAndPermissions = listOf(
|
||||||
|
RoleAndPermissions(
|
||||||
|
role = "default",
|
||||||
|
permissions = listOf(
|
||||||
|
Permissions.startFlow<DummyFlow>(),
|
||||||
|
Permissions.invokeRpc("vaultQueryBy"),
|
||||||
|
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||||
|
Permissions.invokeRpc("vaultQueryByCriteria"))),
|
||||||
|
RoleAndPermissions(
|
||||||
|
role = "admin",
|
||||||
|
permissions = listOf("ALL")
|
||||||
|
)))
|
||||||
|
|
||||||
|
val securityConfig = mapOf(
|
||||||
|
"security" to mapOf(
|
||||||
|
"authService" to mapOf(
|
||||||
|
"dataSource" to mapOf(
|
||||||
|
"type" to "DB",
|
||||||
|
"passwordEncryption" to passwordEncryption.toString(),
|
||||||
|
"connection" to mapOf(
|
||||||
|
"jdbcUrl" to db.jdbcUrl,
|
||||||
|
"username" to "",
|
||||||
|
"password" to "",
|
||||||
|
"driverClassName" to "org.h2.Driver"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"options" to mapOf(
|
||||||
|
"cache" to mapOf(
|
||||||
|
"expireAfterSecs" to cacheExpireAfterSecs,
|
||||||
|
"maxEntries" to 50
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
|
||||||
|
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `login with correct credentials`() {
|
fun `login with correct credentials`() {
|
||||||
@ -65,7 +132,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
|
|||||||
val proxy = it.proxy
|
val proxy = it.proxy
|
||||||
proxy.startFlowDynamic(DummyFlow::class.java)
|
proxy.startFlowDynamic(DummyFlow::class.java)
|
||||||
proxy.startTrackedFlowDynamic(DummyFlow::class.java)
|
proxy.startTrackedFlowDynamic(DummyFlow::class.java)
|
||||||
proxy.startFlow(::DummyFlow)
|
proxy.startFlow(AuthDBTests::DummyFlow)
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
PermissionException::class,
|
PermissionException::class,
|
||||||
"This user should not be authorized to start flow `CashIssueFlow`") {
|
"This user should not be authorized to start flow `CashIssueFlow`") {
|
||||||
@ -92,77 +159,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
|
||||||
@InitiatingFlow
|
|
||||||
class DummyFlow : FlowLogic<Unit>() {
|
|
||||||
@Suspendable
|
|
||||||
override fun call() = Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserAuthServiceEmbedded : UserAuthServiceTest() {
|
|
||||||
|
|
||||||
private val rpcUser = User("user", "foo", permissions = setOf(
|
|
||||||
Permissions.startFlow<DummyFlow>(),
|
|
||||||
Permissions.invokeRpc("vaultQueryBy"),
|
|
||||||
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
|
|
||||||
Permissions.invokeRpc("vaultQueryByCriteria")))
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
val securityConfig = SecurityConfiguration(
|
|
||||||
authService = SecurityConfiguration.AuthService.fromUsers(listOf(rpcUser)))
|
|
||||||
|
|
||||||
val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped())
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides)
|
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|
||||||
|
|
||||||
private val db = UsersDB(
|
|
||||||
name = "SecurityDataSourceTestDB",
|
|
||||||
users = listOf(UserAndRoles(username = "user",
|
|
||||||
password = "foo",
|
|
||||||
roles = listOf("default"))),
|
|
||||||
roleAndPermissions = listOf(
|
|
||||||
RoleAndPermissions(
|
|
||||||
role = "default",
|
|
||||||
permissions = listOf(
|
|
||||||
Permissions.startFlow<DummyFlow>(),
|
|
||||||
Permissions.invokeRpc("vaultQueryBy"),
|
|
||||||
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
|
|
||||||
Permissions.invokeRpc("vaultQueryByCriteria"))),
|
|
||||||
RoleAndPermissions(
|
|
||||||
role = "admin",
|
|
||||||
permissions = listOf("ALL")
|
|
||||||
)))
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
val securityConfig = SecurityConfiguration(
|
|
||||||
authService = SecurityConfiguration.AuthService(
|
|
||||||
dataSource = SecurityConfiguration.AuthService.DataSource(
|
|
||||||
type = AuthDataSourceType.DB,
|
|
||||||
passwordEncryption = PasswordEncryption.NONE,
|
|
||||||
connection = Properties().apply {
|
|
||||||
setProperty("jdbcUrl", db.jdbcUrl)
|
|
||||||
setProperty("username", "")
|
|
||||||
setProperty("password", "")
|
|
||||||
setProperty("driverClassName", "org.h2.Driver")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped())
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides)
|
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Add new users on-the-fly`() {
|
fun `Add new users dynamically`() {
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
ActiveMQSecurityException::class,
|
ActiveMQSecurityException::class,
|
||||||
"Login with incorrect password should fail") {
|
"Login with incorrect password should fail") {
|
||||||
@ -171,7 +169,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
|
|
||||||
db.insert(UserAndRoles(
|
db.insert(UserAndRoles(
|
||||||
username = "user2",
|
username = "user2",
|
||||||
password = "bar",
|
password = encodePassword("bar"),
|
||||||
roles = listOf("default")))
|
roles = listOf("default")))
|
||||||
|
|
||||||
client.start("user2", "bar")
|
client.start("user2", "bar")
|
||||||
@ -181,10 +179,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
fun `Modify user permissions during RPC session`() {
|
fun `Modify user permissions during RPC session`() {
|
||||||
db.insert(UserAndRoles(
|
db.insert(UserAndRoles(
|
||||||
username = "user3",
|
username = "user3",
|
||||||
password = "bar",
|
password = encodePassword("bar"),
|
||||||
roles = emptyList()))
|
roles = emptyList()))
|
||||||
|
|
||||||
|
|
||||||
client.start("user3", "bar").use {
|
client.start("user3", "bar").use {
|
||||||
val proxy = it.proxy
|
val proxy = it.proxy
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
@ -193,6 +190,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
proxy.stateMachinesFeed()
|
proxy.stateMachinesFeed()
|
||||||
}
|
}
|
||||||
db.addRoleToUser("user3", "default")
|
db.addRoleToUser("user3", "default")
|
||||||
|
Thread.sleep(1500)
|
||||||
proxy.stateMachinesFeed()
|
proxy.stateMachinesFeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,13 +199,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
fun `Revoke user permissions during RPC session`() {
|
fun `Revoke user permissions during RPC session`() {
|
||||||
db.insert(UserAndRoles(
|
db.insert(UserAndRoles(
|
||||||
username = "user4",
|
username = "user4",
|
||||||
password = "test",
|
password = encodePassword("test"),
|
||||||
roles = listOf("default")))
|
roles = listOf("default")))
|
||||||
|
|
||||||
client.start("user4", "test").use {
|
client.start("user4", "test").use {
|
||||||
val proxy = it.proxy
|
val proxy = it.proxy
|
||||||
proxy.stateMachinesFeed()
|
proxy.stateMachinesFeed()
|
||||||
db.deleteUser("user4")
|
db.deleteUser("user4")
|
||||||
|
Thread.sleep(1500)
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
PermissionException::class,
|
PermissionException::class,
|
||||||
"This user should not be authorized to call 'nodeInfo'") {
|
"This user should not be authorized to call 'nodeInfo'") {
|
||||||
@ -216,15 +215,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class DummyFlow : FlowLogic<Unit>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() = Unit
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
override fun tearDown() {
|
override fun tearDown() {
|
||||||
db.close()
|
db.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun encodePassword(s: String) = encodePassword(s, passwordEncryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class UserAndRoles(val username: String, val password: String, val roles: List<String>)
|
private data class UserAndRoles(val username: String, val password: String, val roles: List<String>)
|
||||||
private data class RoleAndPermissions(val role: String, val permissions: List<String>)
|
private data class RoleAndPermissions(val role: String, val permissions: List<String>)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manage in-memory DB mocking a users database with the schema expected by Node's security manager
|
||||||
|
*/
|
||||||
private class UsersDB : AutoCloseable {
|
private class UsersDB : AutoCloseable {
|
||||||
|
|
||||||
val jdbcUrl: String
|
val jdbcUrl: String
|
||||||
@ -261,12 +272,6 @@ private class UsersDB : AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteRole(role: String) {
|
|
||||||
session {
|
|
||||||
it.execute("DELETE FROM role_permissions WHERE role_name = '$role'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteUser(username: String) {
|
fun deleteUser(username: String) {
|
||||||
session {
|
session {
|
||||||
it.execute("DELETE FROM users WHERE username = '$username'")
|
it.execute("DELETE FROM users WHERE username = '$username'")
|
||||||
@ -308,3 +313,21 @@ private class UsersDB : AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample of hardcoded hashes to watch for format backward compatibility
|
||||||
|
*/
|
||||||
|
private val hashedPasswords = mapOf(
|
||||||
|
PasswordEncryption.SHIRO_1_CRYPT to mapOf(
|
||||||
|
"foo" to "\$shiro1\$SHA-256$500000\$WSiEVj6q8d02sFcCk1dkoA==\$MBkU/ghdD9ovoDerdzNfkXdP9Bdhmok7tidvVIqGzcA=",
|
||||||
|
"bar" to "\$shiro1\$SHA-256$500000\$Q6dmdY1uVMm0LYAWaOHtCA==\$u7NbFaj9tHf2RTW54jedLPiOiGjJv0RVEPIjVquJuYY=",
|
||||||
|
"test" to "\$shiro1\$SHA-256$500000\$F6CWSFDDxGTlzvREwih8Gw==\$DQhyAPoUw3RdvNYJ1aubCnzEIXm+szGQ3HplaG+euz8="))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A functional object for producing password encoded according to the given scheme.
|
||||||
|
*/
|
||||||
|
private fun encodePassword(s: String, format: PasswordEncryption) = when (format) {
|
||||||
|
PasswordEncryption.NONE -> s
|
||||||
|
PasswordEncryption.SHIRO_1_CRYPT -> hashedPasswords[format]!![s] ?:
|
||||||
|
DefaultPasswordService().encryptPassword(s.toCharArray())
|
||||||
|
}
|
@ -26,6 +26,9 @@ import java.net.ConnectException
|
|||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Ignore
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class SSHServerTest : IntegrationTest() {
|
class SSHServerTest : IntegrationTest() {
|
||||||
companion object {
|
companion object {
|
||||||
@ -33,6 +36,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test()
|
@Test()
|
||||||
fun `ssh server does not start be default`() {
|
fun `ssh server does not start be default`() {
|
||||||
val user = User("u", "p", setOf())
|
val user = User("u", "p", setOf())
|
||||||
@ -54,6 +58,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh server starts when configured`() {
|
fun `ssh server starts when configured`() {
|
||||||
val user = User("u", "p", setOf())
|
val user = User("u", "p", setOf())
|
||||||
@ -74,6 +79,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh server verify credentials`() {
|
fun `ssh server verify credentials`() {
|
||||||
val user = User("u", "p", setOf())
|
val user = User("u", "p", setOf())
|
||||||
@ -97,6 +103,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh respects permissions`() {
|
fun `ssh respects permissions`() {
|
||||||
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
||||||
@ -127,6 +134,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh runs flows`() {
|
fun `ssh runs flows`() {
|
||||||
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
||||||
|
@ -203,8 +203,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
|||||||
data class Options(val cache: Options.Cache?) {
|
data class Options(val cache: Options.Cache?) {
|
||||||
|
|
||||||
// Cache parameters
|
// Cache parameters
|
||||||
data class Cache(val expireAfterSecs: Long, val maxEntries: Long)
|
data class Cache(val expireAfterSecs: Long, val maxEntries: Long) {
|
||||||
|
init {
|
||||||
|
require(expireAfterSecs >= 0) {
|
||||||
|
"Expected positive value for 'cache.expireAfterSecs'"
|
||||||
|
}
|
||||||
|
require(maxEntries > 0) {
|
||||||
|
"Expected positive value for 'cache.maxEntries'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider of users credentials and permissions data
|
// Provider of users credentials and permissions data
|
||||||
@ -228,11 +236,12 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
|||||||
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
|
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromUsers(users: List<User>) = AuthService(
|
fun fromUsers(users: List<User>, encryption: PasswordEncryption = PasswordEncryption.NONE) =
|
||||||
|
AuthService(
|
||||||
dataSource = DataSource(
|
dataSource = DataSource(
|
||||||
type = AuthDataSourceType.INMEMORY,
|
type = AuthDataSourceType.INMEMORY,
|
||||||
users = users,
|
users = users,
|
||||||
passwordEncryption = PasswordEncryption.NONE),
|
passwordEncryption = encryption),
|
||||||
id = AuthServiceId("NODE_CONFIG"))
|
id = AuthServiceId("NODE_CONFIG"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password
|
|||||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||||
import net.corda.node.internal.security.tryAuthenticate
|
import net.corda.node.internal.security.tryAuthenticate
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
|
import net.corda.node.services.config.SecurityConfiguration
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import javax.security.auth.login.FailedLoginException
|
import javax.security.auth.login.FailedLoginException
|
||||||
@ -26,7 +27,7 @@ class RPCSecurityManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Generic RPC call authorization`() {
|
fun `Generic RPC call authorization`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")),
|
permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")),
|
||||||
permissions = setOf(
|
permissions = setOf(
|
||||||
Permissions.invokeRpc(CordaRPCOps::nodeInfo),
|
Permissions.invokeRpc(CordaRPCOps::nodeInfo),
|
||||||
@ -35,7 +36,7 @@ class RPCSecurityManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Flow invocation authorization`() {
|
fun `Flow invocation authorization`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permissions = setOf(Permissions.startFlow<DummyFlow>()),
|
permissions = setOf(Permissions.startFlow<DummyFlow>()),
|
||||||
permitted = setOf(
|
permitted = setOf(
|
||||||
arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"),
|
arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"),
|
||||||
@ -44,21 +45,21 @@ class RPCSecurityManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Check startFlow RPC permission implies startFlowDynamic`() {
|
fun `Check startFlow RPC permission implies startFlowDynamic`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permissions = setOf(Permissions.invokeRpc("startFlow")),
|
permissions = setOf(Permissions.invokeRpc("startFlow")),
|
||||||
permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic")))
|
permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() {
|
fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")),
|
permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")),
|
||||||
permissions = setOf(Permissions.invokeRpc("startTrackedFlow")))
|
permissions = setOf(Permissions.invokeRpc("startTrackedFlow")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Admin authorization`() {
|
fun `Admin authorization`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permissions = setOf("all"),
|
permissions = setOf("all"),
|
||||||
permitted = allActions.map { arrayListOf(it) }.toSet())
|
permitted = allActions.map { arrayListOf(it) }.toSet())
|
||||||
}
|
}
|
||||||
@ -118,9 +119,9 @@ class RPCSecurityManagerTest {
|
|||||||
users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST"))
|
users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkUserPermissions(permissions: Set<String>, permitted: Set<ArrayList<String>>) {
|
private fun checkUserActions(permissions: Set<String>, permitted: Set<ArrayList<String>>) {
|
||||||
val user = User(username = "user", password = "password", permissions = permissions)
|
val user = User(username = "user", password = "password", permissions = permissions)
|
||||||
val userRealms = RPCSecurityManagerImpl.fromUserList(users = listOf(user), id = AuthServiceId("TEST"))
|
val userRealms = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(listOf(user)))
|
||||||
val disabled = allActions.filter { !permitted.contains(listOf(it)) }
|
val disabled = allActions.filter { !permitted.contains(listOf(it)) }
|
||||||
for (subject in listOf(
|
for (subject in listOf(
|
||||||
userRealms.authenticate("user", Password("password")),
|
userRealms.authenticate("user", Password("password")),
|
||||||
|
Loading…
Reference in New Issue
Block a user