Skip to content

Commit

Permalink
Move to a better crypto scheme for passcode encryption (#189)
Browse files Browse the repository at this point in the history
* Move to a better crypto scheme for passcode encryption

* fixed 3 vulnerabilities

* added fixed for maintainabilities from sonarcloud

* fixed encryption

* fixed duplicates

---------

Co-authored-by: Venkat Narayan TR <venkatttwenty1@gmail.com>
  • Loading branch information
karthick-vinod and venkatttwenty1 authored Mar 28, 2024
1 parent 895fba1 commit 8e71985
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 97 deletions.
95 changes: 36 additions & 59 deletions src/main/kotlin/com/hypto/iam/server/apis/CredentialApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ import io.ktor.server.routing.Route
import org.koin.ktor.ext.inject
import java.util.UUID

const val USER_ID_REQUIRED = "userId required"
const val ID_REQUIRED = "id required"

fun getRoutOptionsForGetPatchDeleteCredentials(): List<RouteOption> {
return listOf(
RouteOption(
"/organizations/{organization_id}/users/{userId}/credentials/{id}",
resourceNameIndex = 4,
resourceInstanceIndex = 5,
organizationIdIndex = 1,
),
RouteOption(
"/organizations/{organization_id}/sub_organizations/{sub_organization_name}/users/" +
"{userId}/credentials/{id}",
resourceNameIndex = 6,
resourceInstanceIndex = 7,
organizationIdIndex = 1,
subOrganizationNameIndex = 3,
),
)
}

fun Route.credentialApi() {
val gson: Gson by inject()
val credentialService: CredentialService by inject()
Expand All @@ -46,9 +68,9 @@ fun Route.credentialApi() {
) {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val subOrganizationName = call.parameters["sub_organization_name"]
val userId = call.parameters["userId"] ?: throw IllegalArgumentException("userId required")
val userId = call.parameters["userId"] ?: throw IllegalArgumentException(USER_ID_REQUIRED)
val request = call.receive<CreateCredentialRequest>().validate()

val response =
Expand All @@ -68,30 +90,15 @@ fun Route.credentialApi() {
}

deleteWithPermission(
listOf(
RouteOption(
"/organizations/{organization_id}/users/{userId}/credentials/{id}",
resourceNameIndex = 4,
resourceInstanceIndex = 5,
organizationIdIndex = 1,
),
RouteOption(
"/organizations/{organization_id}/sub_organizations/{sub_organization_name}/users/" +
"{userId}/credentials/{id}",
resourceNameIndex = 6,
resourceInstanceIndex = 7,
organizationIdIndex = 1,
subOrganizationNameIndex = 3,
),
),
getRoutOptionsForGetPatchDeleteCredentials(),
"deleteCredential",
) {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val subOrganizationName = call.parameters["sub_organization_name"]
val userId = call.parameters["userId"] ?: throw IllegalArgumentException("userId required")
val id = UUID.fromString(call.parameters["id"] ?: throw IllegalArgumentException("id required"))
val userId = call.parameters["userId"] ?: throw IllegalArgumentException(USER_ID_REQUIRED)
val id = UUID.fromString(call.parameters["id"] ?: throw IllegalArgumentException(ID_REQUIRED))

val response = credentialService.deleteCredential(organizationId, subOrganizationName, userId, id)

Expand All @@ -103,30 +110,15 @@ fun Route.credentialApi() {
}

getWithPermission(
listOf(
RouteOption(
"/organizations/{organization_id}/users/{userId}/credentials/{id}",
resourceNameIndex = 4,
resourceInstanceIndex = 5,
organizationIdIndex = 1,
),
RouteOption(
"/organizations/{organization_id}/sub_organizations/{sub_organization_name}/users/" +
"{userId}/credentials/{id}",
resourceNameIndex = 6,
resourceInstanceIndex = 7,
organizationIdIndex = 1,
subOrganizationNameIndex = 3,
),
),
getRoutOptionsForGetPatchDeleteCredentials(),
"getCredential",
) {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val subOrganizationName = call.parameters["sub_organization_name"]
val userId = call.parameters["userId"] ?: throw IllegalArgumentException("userId required")
val id = UUID.fromString(call.parameters["id"] ?: throw IllegalArgumentException("id required"))
val userId = call.parameters["userId"] ?: throw IllegalArgumentException(USER_ID_REQUIRED)
val id = UUID.fromString(call.parameters["id"] ?: throw IllegalArgumentException(ID_REQUIRED))

val credential = credentialService.getCredentialWithoutSecret(organizationId, subOrganizationName, userId, id)

Expand Down Expand Up @@ -170,30 +162,15 @@ fun Route.credentialApi() {
}

patchWithPermission(
listOf(
RouteOption(
"/organizations/{organization_id}/users/{userId}/credentials/{id}",
resourceNameIndex = 4,
resourceInstanceIndex = 5,
organizationIdIndex = 1,
),
RouteOption(
"/organizations/{organization_id}/sub_organizations/{sub_organization_name}/users/" +
"{userId}/credentials/{id}",
resourceNameIndex = 6,
resourceInstanceIndex = 7,
organizationIdIndex = 1,
subOrganizationNameIndex = 3,
),
),
getRoutOptionsForGetPatchDeleteCredentials(),
"updateCredential",
) {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val subOrganizationName = call.parameters["sub_organization_name"]
val userId = call.parameters["userId"] ?: throw IllegalArgumentException("userId required")
val id = UUID.fromString(call.parameters["id"] ?: throw IllegalArgumentException("id required"))
val userId = call.parameters["userId"] ?: throw IllegalArgumentException(USER_ID_REQUIRED)
val id = UUID.fromString(call.parameters["id"] ?: throw IllegalArgumentException(ID_REQUIRED))
val request = call.receive<UpdateCredentialRequest>().validate()

val response =
Expand Down
14 changes: 8 additions & 6 deletions src/main/kotlin/com/hypto/iam/server/apis/PolicyApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import io.ktor.server.routing.patch
import io.ktor.server.routing.post
import org.koin.ktor.ext.inject

const val ORGANIZATION_ID_REQUIRED = "organization_id required"

@Suppress("ComplexMethod")
fun Route.policyApi() {
val policyService: PolicyService by inject()
Expand All @@ -41,7 +43,7 @@ fun Route.policyApi() {
post("/organizations/{organization_id}/policies") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val request = call.receive<CreatePolicyRequest>().validate()

val principal = context.principal<UserPrincipal>()!!
Expand All @@ -67,7 +69,7 @@ fun Route.policyApi() {
post("/organizations/{organization_id}/policy_from_template") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val request = call.receive<CreatePolicyFromTemplateRequest>().validate()

val principal = context.principal<UserPrincipal>()!!
Expand Down Expand Up @@ -119,7 +121,7 @@ fun Route.policyApi() {
delete("/organizations/{organization_id}/policies/{name}") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val name = call.parameters["name"] ?: throw IllegalArgumentException("Required name to delete a policy")

val response = policyService.deletePolicy(organizationId, name)
Expand All @@ -139,7 +141,7 @@ fun Route.policyApi() {
get("/organizations/{organization_id}/policies/{name}") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val name =
call.parameters["name"] ?: throw IllegalArgumentException("Required name to get the policy details")

Expand Down Expand Up @@ -172,7 +174,7 @@ fun Route.policyApi() {
),
"getUserPolicy",
) {
val organizationId = call.parameters["organization_id"] ?: throw IllegalArgumentException("organization_id required")
val organizationId = call.parameters["organization_id"] ?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val subOrganizationId = call.parameters["sub_organization_name"]
val userId = call.parameters["user_id"] ?: throw IllegalArgumentException("Required user id to list policies")

Expand Down Expand Up @@ -203,7 +205,7 @@ fun Route.policyApi() {
patch("/organizations/{organization_id}/policies/{name}") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val name =
call.parameters["name"]
?: throw IllegalArgumentException("Required name to update the policy details")
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/hypto/iam/server/apis/ResourceApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fun Route.resourceApi() {
post("/organizations/{organization_id}/resources") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val request = call.receive<CreateResourceRequest>().validate()

val resource =
Expand Down Expand Up @@ -81,7 +81,7 @@ fun Route.resourceApi() {
delete("/organizations/{organization_id}/resources/{name}") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val name = call.parameters["name"] ?: throw IllegalArgumentException("Required name to delete a resource")

val response = resourceService.deleteResource(organizationId, name)
Expand All @@ -101,7 +101,7 @@ fun Route.resourceApi() {
get("/organizations/{organization_id}/resources/{name}") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val name = call.parameters["name"] ?: throw IllegalArgumentException("Required name to get the resource")

val response = resourceService.getResource(organizationId, name)
Expand All @@ -121,7 +121,7 @@ fun Route.resourceApi() {
patch("/organizations/{organization_id}/resources/{name}") {
val organizationId =
call.parameters["organization_id"]
?: throw IllegalArgumentException("organization_id required")
?: throw IllegalArgumentException(ORGANIZATION_ID_REQUIRED)
val name = call.parameters["name"] ?: throw IllegalArgumentException("Required name to update a resource")
val request = call.receive<UpdateResourceRequest>().validate()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ object DbExceptionHandler {
val appExceptions: Map<String, Pair<KClass<out Exception>, String>>,
)

private val customExceptions =
private val customExceptions: Set<KClass<out Throwable>> =
setOf(
BadRequestException::class,
EntityNotFoundException::class,
EntityAlreadyExistsException::class,
)
private val duplicateConstraintRegex = "\"(.*)?\"".toRegex()
private val foreignKeyConstraintRegex = "foreign key constraint \"(.*)?\"".toRegex()
private val duplicateConstraintRegex = "\"(.+)?\"".toRegex()
private val foreignKeyConstraintRegex = "foreign key constraint \"(.+)?\"".toRegex()

private val duplicateExceptions = mapOf<String, Pair<KClass<out Exception>, String>>()

Expand Down
12 changes: 6 additions & 6 deletions src/main/kotlin/com/hypto/iam/server/service/TokenService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,26 +108,26 @@ class TokenServiceImpl : KoinComponent, TokenService {
// Validate claims
val body = jws.body

val issuer: String? = body.get(Claims.ISSUER, String::class.java)
val issuer: String? = body[Claims.ISSUER, String::class.java]
require(issuer != null && issuer == ISSUER) { JWT_INVALID_ISSUER }

// usr is a valid hrn for normal user generated tokens
// In case of delegate tokens, usr field contains the principal to which the token is delegated to.
// The principal of the creator of the token will be present in "obof" claim
val onBehalfOfUser: String? = body.get(ON_BEHALF_CLAIM, String::class.java)
val userHrnStr: String? = body.get(USER_CLAIM, String::class.java)
val onBehalfOfUser: String? = body[ON_BEHALF_CLAIM, String::class.java]
val userHrnStr: String? = body[USER_CLAIM, String::class.java]
require(
userHrnStr != null &&
(HrnFactory.isValid(userHrnStr) || onBehalfOfUser?.let { HrnFactory.isValid(it) } ?: false),
) { JWT_INVALID_USER_HRN }

val organization: String? = body.get(ORGANIZATION_CLAIM, String::class.java)
val organization: String? = body[ORGANIZATION_CLAIM, String::class.java]
require(organization != null) { JWT_INVALID_ORGANIZATION }

val versionNum: String? = body.get(VERSION_CLAIM, String::class.java)
val versionNum: String? = body[VERSION_CLAIM, String::class.java]
require(versionNum != null) { JWT_INVALID_VERSION_NUMBER }

val issuedAt: Date? = body.get(Claims.ISSUED_AT, Date::class.java)
val issuedAt: Date? = body[Claims.ISSUED_AT, Date::class.java]
require(issuedAt is Date && issuedAt.toInstant() <= Instant.now()) {
String.format(JWT_INVALID_ISSUED_AT, issuedAt)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class UserPrincipalServiceImpl : KoinComponent, UserPrincipalService {
): UserPrincipal =
measureTimedValue("TokenService.getUserPrincipalByJwtToken.$deepCheck", logger) {
val token = tokenService.validateJwtToken(tokenCredential.value!!)
val userHrnStr: String = token.body.get(TokenServiceImpl.USER_CLAIM, String::class.java)
val creatorHrnStr: String? = token.body.get(TokenServiceImpl.ON_BEHALF_CLAIM, String::class.java)
val entitlements: String = token.body.get(TokenServiceImpl.ENTITLEMENTS_CLAIM, String::class.java)
val userHrnStr: String = token.body[TokenServiceImpl.USER_CLAIM, String::class.java]
val creatorHrnStr: String? = token.body[TokenServiceImpl.ON_BEHALF_CLAIM, String::class.java]
val entitlements: String = token.body[TokenServiceImpl.ENTITLEMENTS_CLAIM, String::class.java]
val hrn = ResourceHrn(userHrnStr)
val organizationId = hrn.organization
return UserPrincipal(
Expand Down
13 changes: 8 additions & 5 deletions src/main/kotlin/com/hypto/iam/server/service/UsersService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.time.LocalDateTime

const val USER_NOT_FOUND_ERROR_MESSAGE = "Unable to find user"
const val INVALID_ORG_ID_NAME = "Invalid organization id name."

class UsersServiceImpl : KoinComponent, UsersService {
private val principalPolicyService: PrincipalPolicyService by inject()
private val hrnFactory: HrnFactory by inject()
Expand Down Expand Up @@ -211,7 +214,7 @@ class UsersServiceImpl : KoinComponent, UsersService {
val userHrn = ResourceHrn(organizationId, subOrganizationName, IamResources.USER, userName)
val userRecord =
userRepo.findByHrn(userHrn.toString())
?: throw EntityNotFoundException("Unable to find user")
?: throw EntityNotFoundException(USER_NOT_FOUND_ERROR_MESSAGE)
return getUser(userHrn, userRecord)
}

Expand All @@ -231,7 +234,7 @@ class UsersServiceImpl : KoinComponent, UsersService {
userRepo.findByEmail(email)
?: throw EntityNotFoundException("User with email - $email not found")
organizationRepo.findById(user.organizationId)
?: throw EntityNotFoundException("Invalid organization id name. Unable to get user")
?: throw EntityNotFoundException("$INVALID_ORG_ID_NAME $USER_NOT_FOUND_ERROR_MESSAGE")
user
} else {
organizationId ?: throw EntityNotFoundException("Organization id is required")
Expand All @@ -255,7 +258,7 @@ class UsersServiceImpl : KoinComponent, UsersService {
val userHrn = ResourceHrn(organizationId, subOrganizationName ?: "", IamResources.USER, userName)
val userRecord =
userRepo.findByHrn(userHrn.toString())
?: throw EntityNotFoundException("Unable to find user")
?: throw EntityNotFoundException(USER_NOT_FOUND_ERROR_MESSAGE)
if (userRecord.loginAccess != true) {
throw BadRequestException("User - $userName does not have login access")
}
Expand Down Expand Up @@ -283,7 +286,7 @@ class UsersServiceImpl : KoinComponent, UsersService {
val userHrn = ResourceHrn(user.hrn)
val userRecord =
userRepo.findByHrn(userHrn.toString())
?: throw EntityNotFoundException("Unable to find user")
?: throw EntityNotFoundException(USER_NOT_FOUND_ERROR_MESSAGE)
if (userRecord.loginAccess != true) {
throw BadRequestException("User - ${userHrn.resourceInstance} does not have login access")
}
Expand Down Expand Up @@ -367,7 +370,7 @@ class UsersServiceImpl : KoinComponent, UsersService {
): UserPaginatedResponse {
val org =
organizationRepo.findById(organizationId)
?: throw EntityNotFoundException("Invalid organization id name. Unable to get user")
?: throw EntityNotFoundException("$INVALID_ORG_ID_NAME $USER_NOT_FOUND_ERROR_MESSAGE")

val users =
userRepo.fetchUsers(organizationId, subOrganizationName, paginationContext).map { user ->
Expand Down
Loading

0 comments on commit 8e71985

Please sign in to comment.