Skip to content

Commit

Permalink
šŸ› fix: make credentials inactive on deleteUser and fix `org.metadā€¦
Browse files Browse the repository at this point in the history
ā€¦ata == null` bug (#156)

* šŸ”Ø chore: make `credentials` inactive on `deleteUser` and fix `org.metadata == null` bug

* šŸ”§ chore: move guard to `CognitoProviderImpl`
  • Loading branch information
Aravind-Kannan authored Oct 24, 2023
1 parent 64dfae2 commit d57f176
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 8 deletions.
8 changes: 8 additions & 0 deletions src/main/kotlin/com/hypto/iam/server/apis/UsersApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package com.hypto.iam.server.apis
import com.google.gson.Gson
import com.hypto.iam.server.configs.AppConfig
import com.hypto.iam.server.db.repositories.PasscodeRepo
import com.hypto.iam.server.db.repositories.UserAuthRepo
import com.hypto.iam.server.extensions.PaginationContext
import com.hypto.iam.server.models.ChangeUserPasswordRequest
import com.hypto.iam.server.models.CreateUserPasswordRequest
Expand All @@ -25,6 +26,7 @@ import com.hypto.iam.server.security.withPermission
import com.hypto.iam.server.service.PasscodeService
import com.hypto.iam.server.service.PrincipalPolicyService
import com.hypto.iam.server.service.TokenService
import com.hypto.iam.server.service.TokenServiceImpl
import com.hypto.iam.server.service.UsersService
import com.hypto.iam.server.utils.ApplicationIdUtil
import com.hypto.iam.server.utils.HrnFactory
Expand Down Expand Up @@ -52,6 +54,7 @@ fun Route.createUsersApi() {
val passcodeService: PasscodeService by inject()
val idGenerator: ApplicationIdUtil.Generator by inject()
val tokenService: TokenService by inject()
val userAuthRepo: UserAuthRepo by inject()

// **** Create User api ****//

Expand Down Expand Up @@ -95,6 +98,11 @@ fun Route.createUsersApi() {
loginAccess = loginAccess,
policies = policies
)
userAuthRepo.create(
hrn = user.hrn,
providerName = TokenServiceImpl.ISSUER,
authMetadata = null
)

val token = tokenService.generateJwtToken(ResourceHrn(user.hrn))
call.respondText(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,28 @@ object CredentialsRepo : BaseRepo<CredentialsRecord, Credentials, UUID>() {
}

/**
* Fetch records that have `user_hrn = value`
* Fetch records that matches the `id` and `user_hrn`
*/
suspend fun fetchByIdAndUserHrn(id: UUID, value: String): CredentialsRecord? {
return ctx("credentials.fetch_by_id").selectFrom(CREDENTIALS)
.where(CREDENTIALS.ID.eq(id).and(CREDENTIALS.USER_HRN.eq(value)))
.fetchOne()
}

/**
* Updates status to `inactive` for users with `userHrn`
* @return true on successful update. false otherwise.
*/
suspend fun updateStatusForUser(userHrn: String, status: Credential.Status = Credential.Status.inactive): Boolean {
val count = ctx("credentials.update_status_for_user")
.update(CREDENTIALS)
.set(CREDENTIALS.STATUS, status.value)
.set(CREDENTIALS.UPDATED_AT, LocalDateTime.now())
.where(CREDENTIALS.USER_HRN.eq(userHrn))
.execute()
return count > 0
}

/**
* Fetch a unique record that has `refresh_token = value`
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object UserRepo : BaseRepo<UsersRecord, Users, String>() {
): List<UsersRecord> {
return ctx("users.fetchMany").selectFrom(USERS)
.where(USERS.ORGANIZATION_ID.eq(organizationId))
.and(USERS.DELETED.eq(false))
.paginate(USERS.CREATED_AT, paginationContext)
.fetch()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package com.hypto.iam.server.idp

import com.hypto.iam.server.configs.AppConfig
import com.hypto.iam.server.exceptions.InternalException
import com.hypto.iam.server.exceptions.UnknownException
import com.hypto.iam.server.idp.CognitoConstants.ACTION_SUPPRESS
Expand Down Expand Up @@ -69,6 +70,7 @@ object CognitoConstants {
class CognitoIdentityProviderImpl : IdentityProvider, KoinComponent {
private val cognitoClient: CognitoIdentityProviderClient by inject()
private val aliasAttributeTypes = mutableListOf(AliasAttributeType.EMAIL, AliasAttributeType.PREFERRED_USERNAME)
private val appConfig: AppConfig by inject()

override suspend fun createIdentityGroup(name: String, configuration: Configuration): IdentityGroup {
try {
Expand Down Expand Up @@ -121,6 +123,7 @@ class CognitoIdentityProviderImpl : IdentityProvider, KoinComponent {

override suspend fun deleteIdentityGroup(identityGroup: IdentityGroup) {
try {
if (identityGroup == appConfig.cognito) return
val deletePoolRequest = DeleteUserPoolRequest.builder().userPoolId(identityGroup.id).build()
cognitoClient.deleteUserPool(deletePoolRequest)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,10 @@ class OrganizationsServiceImpl : KoinComponent, OrganizationsService {
val org = organizationRepo.findById(id) ?: throw EntityNotFoundException("Organization id - $id not found")
organizationRepo.deleteById(id)

val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
identityProvider.deleteIdentityGroup(identityGroup)
if (org.metadata != null) {
val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
identityProvider.deleteIdentityGroup(identityGroup)
}

return BaseSuccessResponse(true)
}
Expand Down
52 changes: 47 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 @@ -4,6 +4,7 @@ package com.hypto.iam.server.service

import com.google.gson.Gson
import com.hypto.iam.server.configs.AppConfig
import com.hypto.iam.server.db.repositories.CredentialsRepo
import com.hypto.iam.server.db.repositories.OrganizationRepo
import com.hypto.iam.server.db.repositories.PasscodeRepo
import com.hypto.iam.server.db.repositories.UserAuthRepo
Expand Down Expand Up @@ -47,6 +48,7 @@ class UsersServiceImpl : KoinComponent, UsersService {
private val appConfig: AppConfig by inject()
private val passcodeRepo: PasscodeRepo by inject()
private val userAuthRepo: UserAuthRepo by inject()
private val credentialRepo: CredentialsRepo by inject()

@Suppress("CyclomaticComplexMethod")
override suspend fun createUser(
Expand Down Expand Up @@ -147,13 +149,25 @@ class UsersServiceImpl : KoinComponent, UsersService {
password = password ?: throw BadRequestException("Password is required"),
)

val identityGroup = if (org.metadata != null) {
gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
} else {
organizationRepo.update(
organizationId,
null,
null,
appConfig.cognito
)
appConfig.cognito
}

identityProvider.createUser(
RequestContext(
organizationId = organizationId,
requestedPrincipal = createdBy ?: "unknown user",
verified
),
gson.fromJson(org.metadata.data(), IdentityGroup::class.java),
identityGroup,
passwordCredentials
)
}
Expand Down Expand Up @@ -201,6 +215,10 @@ class UsersServiceImpl : KoinComponent, UsersService {
throw BadRequestException("User - $userName does not have login access")
}

if (userAuthRepo.fetchByUserHrnAndProviderName(userHrn.toString(), TokenServiceImpl.ISSUER) == null) {
throw BadRequestException("User does not have password access")
}

val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
identityProvider.authenticate(identityGroup, userName, oldPassword)
identityProvider.setUserPassword(identityGroup, userName, newPassword)
Expand All @@ -222,7 +240,18 @@ class UsersServiceImpl : KoinComponent, UsersService {
throw BadRequestException("User - ${userHrn.resourceInstance} does not have login access")
}

val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
val identityGroup = if (org.metadata != null) {
gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
} else {
organizationRepo.update(
organizationId,
null,
null,
appConfig.cognito
)
appConfig.cognito
}

identityProvider.setUserPassword(identityGroup, user.username, password)
passcodeRepo.deleteByEmailAndPurpose(user.email!!, VerifyEmailRequest.Purpose.reset)
return BaseSuccessResponse(true)
Expand Down Expand Up @@ -311,7 +340,10 @@ class UsersServiceImpl : KoinComponent, UsersService {
?: throw EntityNotFoundException("User not found")

val userStatus = status?.toUserStatus()
if (userRecord.loginAccess == true && org.metadata != null) {
if (userRecord.loginAccess == true &&
org.metadata != null &&
userAuthRepo.fetchByUserHrnAndProviderName(userHrn.toString(), TokenServiceImpl.ISSUER) != null
) {
val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)

identityProvider.updateUser(
Expand Down Expand Up @@ -340,11 +372,17 @@ class UsersServiceImpl : KoinComponent, UsersService {
val userRecord = userRepo.findByHrn(userHrn.toString())
?: throw EntityNotFoundException("User not found")

if (userRecord.loginAccess == true) {
if (userRecord.loginAccess == true &&
org.metadata != null &&
userAuthRepo.fetchByUserHrnAndProviderName(userHrn.toString(), TokenServiceImpl.ISSUER) != null
) {
val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
identityProvider.deleteUser(identityGroup, userName)
}
userRepo.delete(userHrn.toString())
txMan.wrap {
credentialRepo.updateStatusForUser(userHrn.toString())
userRepo.delete(userHrn.toString())
}

return BaseSuccessResponse(true)
}
Expand All @@ -359,6 +397,10 @@ class UsersServiceImpl : KoinComponent, UsersService {
throw BadRequestException("User - $userName does not have login access")
}

if (userAuthRepo.fetchByUserHrnAndProviderName(userRecord.hrn, TokenServiceImpl.ISSUER) == null) {
throw AuthenticationException("User - $userName does not have password access")
}

val identityGroup = gson.fromJson(org.metadata.data(), IdentityGroup::class.java)
identityProvider.authenticate(identityGroup, userName, password)

Expand Down

0 comments on commit d57f176

Please sign in to comment.