Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Onboarding & other UI fixes, security improvements #2036

Merged
merged 10 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import io.tolgee.configuration.tolgee.GithubAuthenticationProperties
import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.constants.Message
import io.tolgee.exceptions.AuthenticationException
import io.tolgee.model.Invitation
import io.tolgee.model.UserAccount
import io.tolgee.security.authentication.JwtService
import io.tolgee.security.payload.JwtAuthenticationResponse
import io.tolgee.service.InvitationService
import io.tolgee.service.security.SignUpService
import io.tolgee.service.security.UserAccountService
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
Expand All @@ -24,8 +23,8 @@ class GithubOAuthDelegate(
private val jwtService: JwtService,
private val userAccountService: UserAccountService,
private val restTemplate: RestTemplate,
private val properties: TolgeeProperties,
private val invitationService: InvitationService
properties: TolgeeProperties,
private val signUpService: SignUpService
) {
private val githubConfigurationProperties: GithubAuthenticationProperties = properties.authentication.github

Expand Down Expand Up @@ -72,25 +71,15 @@ class GithubOAuthDelegate(
throw AuthenticationException(Message.USERNAME_ALREADY_EXISTS)
}

var invitation: Invitation? = null
if (invitationCode == null) {
if (!properties.authentication.registrationsAllowed) {
throw AuthenticationException(Message.REGISTRATIONS_NOT_ALLOWED)
}
} else {
invitation = invitationService.getInvitation(invitationCode)
}

val newUserAccount = UserAccount()
newUserAccount.username = githubEmail
newUserAccount.name = userResponse.name ?: userResponse.login
newUserAccount.thirdPartyAuthId = userResponse.id
newUserAccount.thirdPartyAuthType = "github"
newUserAccount.accountType = UserAccount.AccountType.THIRD_PARTY
userAccountService.createUser(newUserAccount)
if (invitation != null) {
invitationService.accept(invitation.code, newUserAccount)
}

signUpService.signUp(newUserAccount, invitationCode, null)

newUserAccount
}
val jwt = jwtService.emitToken(user.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import io.tolgee.configuration.tolgee.GoogleAuthenticationProperties
import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.constants.Message
import io.tolgee.exceptions.AuthenticationException
import io.tolgee.model.Invitation
import io.tolgee.model.UserAccount
import io.tolgee.security.authentication.JwtService
import io.tolgee.security.payload.JwtAuthenticationResponse
import io.tolgee.service.InvitationService
import io.tolgee.service.security.SignUpService
import io.tolgee.service.security.UserAccountService
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
Expand All @@ -23,8 +22,8 @@ class GoogleOAuthDelegate(
private val jwtService: JwtService,
private val userAccountService: UserAccountService,
private val restTemplate: RestTemplate,
private val properties: TolgeeProperties,
private val invitationService: InvitationService
properties: TolgeeProperties,
private val signUpService: SignUpService
) {
private val googleConfigurationProperties: GoogleAuthenticationProperties = properties.authentication.google

Expand Down Expand Up @@ -78,26 +77,14 @@ class GoogleOAuthDelegate(
throw AuthenticationException(Message.USERNAME_ALREADY_EXISTS)
}

var invitation: Invitation? = null
if (invitationCode == null) {
if (!properties.authentication.registrationsAllowed) {
throw AuthenticationException(Message.REGISTRATIONS_NOT_ALLOWED)
}
} else {
invitation = invitationService.getInvitation(invitationCode)
}

val newUserAccount = UserAccount()
newUserAccount.username = userResponse.email
?: throw AuthenticationException(Message.THIRD_PARTY_AUTH_NO_EMAIL)
newUserAccount.name = userResponse.name ?: (userResponse.given_name + " " + userResponse.family_name)
newUserAccount.thirdPartyAuthId = userResponse.sub
newUserAccount.thirdPartyAuthType = "google"
newUserAccount.accountType = UserAccount.AccountType.THIRD_PARTY
userAccountService.createUser(newUserAccount)
if (invitation != null) {
invitationService.accept(invitation.code, newUserAccount)
}
signUpService.signUp(newUserAccount, invitationCode, null)

newUserAccount
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import io.tolgee.configuration.tolgee.OAuth2AuthenticationProperties
import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.constants.Message
import io.tolgee.exceptions.AuthenticationException
import io.tolgee.model.Invitation
import io.tolgee.model.UserAccount
import io.tolgee.security.authentication.JwtService
import io.tolgee.security.payload.JwtAuthenticationResponse
import io.tolgee.service.InvitationService
import io.tolgee.service.security.SignUpService
import io.tolgee.service.security.UserAccountService
import org.slf4j.LoggerFactory
import org.springframework.http.HttpEntity
Expand All @@ -27,8 +26,8 @@ class OAuth2Delegate(
private val jwtService: JwtService,
private val userAccountService: UserAccountService,
private val restTemplate: RestTemplate,
private val properties: TolgeeProperties,
private val invitationService: InvitationService
properties: TolgeeProperties,
private val signUpService: SignUpService
) {
private val oauth2ConfigurationProperties: OAuth2AuthenticationProperties = properties.authentication.oauth2
private val logger = LoggerFactory.getLogger(this::class.java)
Expand Down Expand Up @@ -96,15 +95,6 @@ class OAuth2Delegate(
throw AuthenticationException(Message.USERNAME_ALREADY_EXISTS)
}

var invitation: Invitation? = null
if (invitationCode == null) {
if (!properties.authentication.registrationsAllowed) {
throw AuthenticationException(Message.REGISTRATIONS_NOT_ALLOWED)
}
} else {
invitation = invitationService.getInvitation(invitationCode)
}

val newUserAccount = UserAccount()
newUserAccount.username =
userResponse.email ?: throw AuthenticationException(Message.THIRD_PARTY_AUTH_NO_EMAIL)
Expand All @@ -120,10 +110,8 @@ class OAuth2Delegate(
newUserAccount.thirdPartyAuthId = userResponse.sub
newUserAccount.thirdPartyAuthType = "oauth2"
newUserAccount.accountType = UserAccount.AccountType.THIRD_PARTY
userAccountService.createUser(newUserAccount)
if (invitation != null) {
invitationService.accept(invitation.code, newUserAccount)
}
signUpService.signUp(newUserAccount, invitationCode, null)

newUserAccount
}
val jwt = jwtService.emitToken(user.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.tolgee.commandLineRunners

import io.tolgee.configuration.tolgee.InternalProperties
import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.dtos.request.auth.SignUpDto
import io.tolgee.dtos.request.organization.OrganizationDto
import io.tolgee.model.UserAccount
import io.tolgee.security.InitialPasswordManager
Expand Down Expand Up @@ -45,10 +44,29 @@ class InitialUserCreatorCommandLineRunner(
fun createInitialUser() {
logger.info("Creating initial user...")
val initialUsername = properties.authentication.initialUsername

// Check if the account already exists.
// This can only be the case on Tolgee 3.x series and should be removed on Tolgee 4.
val candidate = userAccountService.findActive(initialUsername)
if (candidate != null) {
candidate.isInitialUser = true
userAccountService.save(candidate)
return
}

val initialPassword = initialPasswordManager.initialPassword
val user = userAccountService.createInitialUser(
SignUpDto(email = initialUsername, password = initialPassword, name = initialUsername),
)
val user = UserAccount(
username = initialUsername,
password = passwordEncoder.encode(initialPassword),
name = initialUsername,
role = UserAccount.Role.ADMIN,
).apply {
passwordChanged = false
isInitialUser = true
}

userAccountService.createUser(userAccount = user)
userAccountService.transferLegacyNoAuthUser()

// If the user was already existing, it may already have assigned orgs.
// To avoid conflicts, we only create the org if the user doesn't have any.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class DemoProjectCreator(
val project = Project().apply {
name = "Demo project"
this@apply.organizationOwner = organization
this.description = "This is a demo project of an packing list app"
this.description = "This is a demo project of a packing list app"
}
projectService.save(project)
setAvatar(project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.tolgee.development

import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.dtos.request.LanguageDto
import io.tolgee.dtos.request.auth.SignUpDto
import io.tolgee.dtos.request.organization.OrganizationDto
import io.tolgee.model.ApiKey
import io.tolgee.model.Language
Expand All @@ -25,6 +24,7 @@ import io.tolgee.service.security.UserAccountService
import io.tolgee.util.SlugGenerator
import io.tolgee.util.executeInNewTransaction
import jakarta.persistence.EntityManager
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.annotation.Transactional
Expand All @@ -45,6 +45,7 @@ class DbPopulatorReal(
private val apiKeyService: ApiKeyService,
private val languageStatsService: LanguageStatsService,
private val platformTransactionManager: PlatformTransactionManager,
private val passwordEncoder: PasswordEncoder
) {
private lateinit var de: Language
private lateinit var en: Language
Expand All @@ -59,12 +60,17 @@ class DbPopulatorReal(

fun createUserIfNotExists(username: String, password: String? = null, name: String? = null): UserAccount {
return userAccountService.findActive(username) ?: let {
val signUpDto = SignUpDto(
name = name ?: username, email = username,
password = password
?: initialPasswordManager.initialPassword

val rawPassword = password
?: initialPasswordManager.initialPassword

userAccountService.createUser(
UserAccount(
name = name ?: username,
username = username,
password = passwordEncoder.encode(rawPassword)
)
)
userAccountService.createUser(signUpDto)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import jakarta.validation.constraints.Size
@JsonIgnoreProperties(ignoreUnknown = true)
data class UserMfaRecoveryRequestDto(
@field:NotBlank
@field:Size(max = 50)
@field:Size(max = 200)
JanCizmar marked this conversation as resolved.
Show resolved Hide resolved
var password: String = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ data class UserTotpEnableRequestDto(
var otp: String = "",

@field:NotBlank
@field:Size(max = 50)
@field:Size(max = 200)
var password: String = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import jakarta.validation.constraints.Size
@JsonIgnoreProperties(ignoreUnknown = true)
data class UserUpdatePasswordRequestDto(
@field:NotBlank
@field:Size(max = 50)
@field:Size(max = 200)
var currentPassword: String = "",

@field:Size(min = 8, max = 50)
@field:Size(min = 8, max = 200)
var password: String = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ data class UserUpdateRequestDto(
@field:NotBlank
var email: String = "",

@field:Size(max = 50)
@field:Size(max = 200)
var currentPassword: String? = null,

@Schema(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ data class ResetPassword(
@field:NotBlank
var code: String? = null,

@field:Size(min = 8, max = 50)
@field:Size(min = 8, max = 200)
var password: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ data class SignUpDto(
@field:Size(min = 3, max = 50)
var organizationName: String? = null,

@field:Size(min = 8, max = 50)
@field:Size(min = 8, max = 200)
@field:NotBlank
var password: String? = null,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package io.tolgee.service
import io.tolgee.component.email.InvitationEmailSender
import io.tolgee.component.reporting.BusinessEventPublisher
import io.tolgee.component.reporting.OnBusinessEventToCaptureEvent
import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.constants.Message
import io.tolgee.dtos.cacheable.UserAccountDto
import io.tolgee.dtos.misc.CreateInvitationParams
import io.tolgee.dtos.misc.CreateOrganizationInvitationParams
import io.tolgee.dtos.misc.CreateProjectInvitationParams
import io.tolgee.exceptions.AuthenticationException
import io.tolgee.exceptions.BadRequestException
import io.tolgee.model.Invitation
import io.tolgee.model.Organization
Expand Down Expand Up @@ -35,7 +37,8 @@ class InvitationService @Autowired constructor(
private val organizationRoleService: OrganizationRoleService,
private val permissionService: PermissionService,
private val invitationEmailSender: InvitationEmailSender,
private val businessEventPublisher: BusinessEventPublisher
private val businessEventPublisher: BusinessEventPublisher,
private val tolgeeProperties: TolgeeProperties
) : Logging {
@Transactional
fun create(params: CreateProjectInvitationParams): Invitation {
Expand Down Expand Up @@ -231,4 +234,15 @@ class InvitationService @Autowired constructor(
fun userOrInvitationWithEmailExists(email: String, organization: Organization): Boolean {
return invitationRepository.countByUserOrInvitationWithEmailAndOrganization(email, organization) > 0
}

@Transactional
fun getInvitationOnRegistration(invitationCode: String?): Invitation? {
if (invitationCode == null) {
if (!tolgeeProperties.authentication.registrationsAllowed) {
throw AuthenticationException(Message.REGISTRATIONS_NOT_ALLOWED)
}
JanCizmar marked this conversation as resolved.
Show resolved Hide resolved
return null
}
return getInvitation(invitationCode)
}
}
Loading
Loading