Skip to content

Commit

Permalink
CORE-20758: add DB implementation for properties (#6291)
Browse files Browse the repository at this point in the history
  • Loading branch information
aadhavskthvl authored Jul 24, 2024
1 parent f78fe82 commit edfe1ee
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ fun <T : Any?> withPermissionManager(
title = "Permission management operation timed out.",
exceptionDetails = ExceptionDetails(e::class.java.name, "${e.message}")
)
} catch (e: EntityNotFoundException) {
logger.warn("Resource not found during permission management operation")
throw ResourceNotFoundException(
title = "Resource not found during permission management operation",
ExceptionDetails(e::class.java.name, "${e.message}")
)
} catch (e: Exception) {
logger.warn("Unexpected error during permission management operation.", e)
throw InternalServerException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ enum class RestPermissionOperation {
DELETE_PERMISSION_FROM_ROLE,
DELETE_ROLE_FROM_USER,
DELETE_ROLE_FROM_GROUP,
DELETE_PROPERTY_FROM_USER
}
1 change: 1 addition & 0 deletions libs/permissions/permission-manager-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dependencies {
implementation "net.corda:corda-config-schema"
implementation "net.corda:corda-crypto"

implementation project(":libs:permissions:permission-common")
implementation project(":libs:permissions:permission-manager")
implementation project(":libs:permissions:permission-management-cache")
implementation project(":libs:permissions:permission-validation-cache")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import net.corda.data.permissions.management.user.DeleteUserRequest
import net.corda.data.permissions.management.user.RemovePropertyFromUserRequest
import net.corda.data.permissions.management.user.RemoveRoleFromUserRequest
import net.corda.libs.configuration.SmartConfig
import net.corda.libs.permissions.common.exception.EntityNotFoundException
import net.corda.libs.permissions.management.cache.PermissionManagementCache
import net.corda.libs.permissions.manager.PermissionUserManager
import net.corda.libs.permissions.manager.impl.SmartConfigUtil.getEndpointTimeout
Expand Down Expand Up @@ -199,7 +200,8 @@ class PermissionUserManagerImpl(
"Permission validation cache is null."
}

val cachedPermissionSummary = permissionValidationCache.getPermissionSummary(permissionSummaryRequestDto.userLogin) ?: return null
val cachedPermissionSummary =
permissionValidationCache.getPermissionSummary(permissionSummaryRequestDto.userLogin) ?: return null

return UserPermissionSummaryResponseDto(
cachedPermissionSummary.loginName,
Expand Down Expand Up @@ -245,7 +247,8 @@ class PermissionUserManagerImpl(
val permissionManagementCache = checkNotNull(permissionManagementCacheRef.get()) {
"Permission management cache is null."
}
val cachedUser: User = permissionManagementCache.getUser(getUserPropertiesRequestDto.loginName) ?: return emptySet()
val cachedUser: User = permissionManagementCache.getUser(getUserPropertiesRequestDto.loginName)
?: throw EntityNotFoundException("Invalid user.")
return cachedUser.properties.map { it.convertToResponseDto() }.toSet()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import net.corda.data.permissions.management.user.RemovePropertyFromUserRequest
import net.corda.data.permissions.management.user.RemoveRoleFromUserRequest
import net.corda.libs.configuration.SmartConfig
import net.corda.libs.configuration.SmartConfigImpl
import net.corda.libs.permissions.common.exception.EntityNotFoundException
import net.corda.libs.permissions.management.cache.PermissionManagementCache
import net.corda.libs.permissions.manager.exception.UnexpectedPermissionResponseException
import net.corda.libs.permissions.manager.request.AddPropertyToUserRequestDto
Expand All @@ -27,7 +28,6 @@ import net.corda.libs.permissions.manager.request.GetUserRequestDto
import net.corda.libs.permissions.manager.request.GetUsersByPropertyRequestDto
import net.corda.libs.permissions.manager.request.RemovePropertyFromUserRequestDto
import net.corda.libs.permissions.manager.request.RemoveRoleFromUserRequestDto
import net.corda.libs.permissions.manager.response.PropertyResponseDto
import net.corda.libs.permissions.manager.response.UserResponseDto
import net.corda.libs.permissions.validation.cache.PermissionValidationCache
import net.corda.messaging.api.publisher.RPCSender
Expand Down Expand Up @@ -540,11 +540,19 @@ class PermissionUserManagerImplTest {
}

@Test
fun `get user properties returns empty set when user does not exist`() {
whenever(permissionManagementCache.getUser("invalid-user-login-name")).thenReturn(null)
fun `get user properties throws when user does not exist`() {
val future = mock<CompletableFuture<PermissionManagementResponse>>()
whenever(future.getOrThrow(defaultTimeout)).thenThrow(EntityNotFoundException("Invalid user."))

val result = manager.getUserProperties(getUserPropertiesRequestDto)
assertEquals(emptySet<PropertyResponseDto>(), result)
val capture = argumentCaptor<PermissionManagementRequest>()
whenever(rpcSender.sendRequest(capture.capture())).thenReturn(future)

val requestDto = GetUserPropertiesRequestDto("requestUserId", "user-login1")

val e = assertThrows<EntityNotFoundException> {
manager.getUserProperties(requestDto)
}
assertEquals("Invalid user.", e.message)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import net.corda.data.permissions.management.permission.CreatePermissionRequest
import net.corda.data.permissions.management.role.AddPermissionToRoleRequest
import net.corda.data.permissions.management.role.CreateRoleRequest
import net.corda.data.permissions.management.role.RemovePermissionFromRoleRequest
import net.corda.data.permissions.management.user.AddPropertyToUserRequest
import net.corda.data.permissions.management.user.AddRoleToUserRequest
import net.corda.data.permissions.management.user.ChangeUserPasswordRequest
import net.corda.data.permissions.management.user.CreateUserRequest
import net.corda.data.permissions.management.user.DeleteUserRequest
import net.corda.data.permissions.management.user.RemovePropertyFromUserRequest
import net.corda.data.permissions.management.user.RemoveRoleFromUserRequest
import net.corda.libs.permissions.storage.reader.PermissionStorageReader
import net.corda.libs.permissions.storage.writer.PermissionStorageWriterProcessor
Expand Down Expand Up @@ -121,6 +123,16 @@ class PermissionStorageWriterProcessorImpl(
permissionStorageReader.reconcilePermissionSummaries()
avroUser
}
is AddPropertyToUserRequest -> {
val avroUser = userWriter.addPropertyToUser(permissionRequest, request.requestUserId)
permissionStorageReader.publishUpdatedUser(avroUser)
avroUser
}
is RemovePropertyFromUserRequest -> {
val avroUser = userWriter.removePropertyFromUser(permissionRequest, request.requestUserId)
permissionStorageReader.publishUpdatedUser(avroUser)
avroUser
}
is AddPermissionToRoleRequest -> {
val avroRole = roleWriter.addPermissionToRole(permissionRequest, request.requestUserId)
permissionStorageReader.publishUpdatedRole(avroRole)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package net.corda.libs.permissions.storage.writer.impl.user

import net.corda.data.permissions.management.user.AddPropertyToUserRequest
import net.corda.data.permissions.management.user.AddRoleToUserRequest
import net.corda.data.permissions.management.user.ChangeUserPasswordRequest
import net.corda.data.permissions.management.user.CreateUserRequest
import net.corda.data.permissions.management.user.DeleteUserRequest
import net.corda.data.permissions.management.user.RemovePropertyFromUserRequest
import net.corda.data.permissions.management.user.RemoveRoleFromUserRequest
import net.corda.data.permissions.User as AvroUser

Expand Down Expand Up @@ -50,4 +52,20 @@ interface UserWriter {
* @param requestUserId ID of the user who made the request.
*/
fun removeRoleFromUser(request: RemoveRoleFromUserRequest, requestUserId: String): AvroUser

/**
* Add a property to a User and return its Avro representation.
*
* @param request AddPropertyToUserRequest containing the Property to add to a User.
* @param requestUserId ID of the user who made the request.
*/
fun addPropertyToUser(request: AddPropertyToUserRequest, requestUserId: String): AvroUser

/**
* Remove a property from a User and return its Avro representation.
*
* @param request RemovePropertyFromUserRequest containing the Property to remove from a User.
* @param requestUserId ID of the user who made the request.
*/
fun removePropertyFromUser(request: RemovePropertyFromUserRequest, requestUserId: String): AvroUser
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package net.corda.libs.permissions.storage.writer.impl.user.impl

import net.corda.data.permissions.management.user.AddPropertyToUserRequest
import net.corda.data.permissions.management.user.AddRoleToUserRequest
import net.corda.data.permissions.management.user.ChangeUserPasswordRequest
import net.corda.data.permissions.management.user.CreateUserRequest
import net.corda.data.permissions.management.user.DeleteUserRequest
import net.corda.data.permissions.management.user.RemovePropertyFromUserRequest
import net.corda.data.permissions.management.user.RemoveRoleFromUserRequest
import net.corda.libs.permissions.storage.common.converter.toAvroUser
import net.corda.libs.permissions.storage.writer.impl.user.UserWriter
Expand All @@ -15,6 +17,7 @@ import net.corda.permissions.model.RestPermissionOperation
import net.corda.permissions.model.Role
import net.corda.permissions.model.RoleUserAssociation
import net.corda.permissions.model.User
import net.corda.permissions.model.UserProperty
import net.corda.utilities.debug
import org.slf4j.LoggerFactory
import java.time.Instant
Expand Down Expand Up @@ -117,6 +120,42 @@ class UserWriterImpl(
}
}

override fun addPropertyToUser(
request: AddPropertyToUserRequest,
requestUserId: String
): AvroUser {
log.debug { "Received request to add Property ${request.properties} to User ${request.loginName}" }
return entityManagerFactory.transaction { entityManager ->
val validator = EntityValidationUtil(entityManager)
val user = validator.validateAndGetUniqueUser(request.loginName)
val properties = request.properties.map {
UserProperty(
id = UUID.randomUUID().toString(),
updateTimestamp = Instant.now(),
userRef = user,
key = it.key,
value = it.value
)
}
val resultUser = assignUserProperty(entityManager, requestUserId, user, properties)
resultUser.toAvroUser()
}
}

override fun removePropertyFromUser(
request: RemovePropertyFromUserRequest,
requestUserId: String
): AvroUser {
log.debug { "Received request to remove Property with key ${request.propertyKey} from User ${request.loginName}" }
return entityManagerFactory.transaction { entityManager ->
val validator = EntityValidationUtil(entityManager)
val user = validator.validateAndGetUniqueUser(request.loginName)
val property = validator.validateAndGetPropertyByKey(user, request.propertyKey)
val resultUser = removeUserProperty(entityManager, requestUserId, user, property)
resultUser.toAvroUser()
}
}

private fun persistNewUser(
request: CreateUserRequest,
parentGroup: Group?,
Expand Down Expand Up @@ -177,7 +216,12 @@ class UserWriterImpl(
return user
}

private fun persistUserRoleAssociation(entityManager: EntityManager, requestUserId: String, user: User, role: Role): User {
private fun persistUserRoleAssociation(
entityManager: EntityManager,
requestUserId: String,
user: User,
role: Role
): User {
val updateTimestamp = Instant.now()
val association = RoleUserAssociation(UUID.randomUUID().toString(), role, user, updateTimestamp)
val changeAudit = ChangeAudit(
Expand Down Expand Up @@ -223,4 +267,49 @@ class UserWriterImpl(

return user
}

private fun assignUserProperty(
entityManager: EntityManager,
requestUserId: String,
user: User,
properties: List<UserProperty>
): User {
val updateTimestamp = Instant.now()
properties.forEach {
val changeAudit = ChangeAudit(
id = UUID.randomUUID().toString(),
updateTimestamp = updateTimestamp,
actorUser = requestUserId,
changeType = RestPermissionOperation.ADD_PROPERTY_TO_USER,
details = "Property with key '${it.key}' and value '${it.value}' added to " +
"User '${user.loginName}' by '$requestUserId'"
)

user.userProperties.add(it)
entityManager.merge(user)
entityManager.persist(changeAudit)
}
return user
}

private fun removeUserProperty(
entityManager: EntityManager,
requestUserId: String,
user: User,
property: UserProperty
): User {
val updateTimestamp = Instant.now()
val changeAudit = ChangeAudit(
id = UUID.randomUUID().toString(),
updateTimestamp = updateTimestamp,
actorUser = requestUserId,
changeType = RestPermissionOperation.DELETE_PROPERTY_FROM_USER,
details = "Property with key '${property.key}' removed from User '${user.loginName}' by '$requestUserId'"
)

user.userProperties.remove(property)
entityManager.merge(user)
entityManager.persist(changeAudit)
return user
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import net.corda.permissions.model.RoleGroupAssociation
import net.corda.permissions.model.RolePermissionAssociation
import net.corda.permissions.model.RoleUserAssociation
import net.corda.permissions.model.User
import net.corda.permissions.model.UserProperty
import javax.persistence.EntityManager

class EntityValidationUtil(private val entityManager: EntityManager) {
Expand Down Expand Up @@ -127,6 +128,15 @@ class EntityValidationUtil(private val entityManager: EntityManager) {
}
}

fun validateAndGetPropertyByKey(user: User, propertyKey: String): UserProperty {
val value = user.userProperties.singleOrNull { it.key == propertyKey }
if (value == null) {
throw EntityAssociationDoesNotExistException("Property '$propertyKey' is not assigned to User '${user.loginName}'.")
} else {
return value
}
}

fun validateUserDoesNotAlreadyExist(loginName: String) {
val count = entityManager
.createQuery("SELECT count(1) FROM User WHERE loginName = :loginName")
Expand Down
Loading

0 comments on commit edfe1ee

Please sign in to comment.