All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.nemerosa.ontrack.it.AbstractServiceTestSupport.kt Maven / Gradle / Ivy

There is a newer version: 4.4.5
Show newest version
package net.nemerosa.ontrack.it

import net.nemerosa.ontrack.model.security.*
import net.nemerosa.ontrack.model.settings.CachedSettingsService
import net.nemerosa.ontrack.model.settings.SecuritySettings
import net.nemerosa.ontrack.model.settings.SettingsManagerService
import net.nemerosa.ontrack.model.structure.*
import net.nemerosa.ontrack.model.structure.Branch.Companion.of
import net.nemerosa.ontrack.model.structure.Build.Companion.of
import net.nemerosa.ontrack.model.structure.ID.Companion.of
import net.nemerosa.ontrack.model.structure.NameDescription.Companion.nd
import net.nemerosa.ontrack.model.structure.Project.Companion.of
import net.nemerosa.ontrack.model.structure.PromotionRun.Companion.of
import net.nemerosa.ontrack.model.structure.Signature.Companion.of
import net.nemerosa.ontrack.test.TestUtils.uid
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.core.context.SecurityContext
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.context.SecurityContextImpl
import java.util.concurrent.Callable


abstract class AbstractServiceTestSupport : AbstractITTestSupport() {

    @Autowired
    protected lateinit var accountService: AccountService

    @Autowired
    protected lateinit var structureService: StructureService

    @Autowired
    protected lateinit var propertyService: PropertyService

    @Autowired
    protected lateinit var settingsManagerService: SettingsManagerService

    @Autowired
    protected lateinit var cachedSettingsService: CachedSettingsService

    @Autowired
    protected lateinit var securityService: SecurityService

    @Autowired
    protected lateinit var rolesService: RolesService

    protected fun doCreateAccountGroup(): AccountGroup {
        return asUser().with(AccountGroupManagement::class.java).call {
            val name = uid("G")
            accountService.createGroup(
                    AccountGroupInput(name, "")
            )
        }
    }

    protected fun doCreateAccount(accountGroup: AccountGroup): Account {
        return doCreateAccount(listOf(accountGroup))
    }

    protected fun doCreateAccount(accountGroups: List = emptyList()): Account {
        return asUser().with(AccountManagement::class.java).call {
            val name = uid("A")
            accountService.create(
                    AccountInput(
                            name,
                            "Test $name",
                            "[email protected]",
                            "test",
                            accountGroups.map { it.id() }
                    )
            )
        }
    }

    protected fun doCreateAccountWithGlobalRole(role: String): Account {
        val account = doCreateAccount()
        return asUser().with(AccountManagement::class.java).call {
            accountService.saveGlobalPermission(
                    PermissionTargetType.ACCOUNT,
                    account.id(),
                    PermissionInput(role)
            )
            account
        }
    }

    protected fun doCreateAccountWithProjectRole(project: Project, role: String): Account {
        val account = doCreateAccount()
        return asUser().with(project, ProjectAuthorisationMgt::class.java).call {
            accountService.saveProjectPermission(
                    project.id,
                    PermissionTargetType.ACCOUNT,
                    account.id(),
                    PermissionInput(role)
            )
            account
        }
    }

    protected fun doCreateAccountGroupWithGlobalRole(role: String): AccountGroup {
        val group = doCreateAccountGroup()
        return asUser().with(AccountGroupManagement::class.java).call {
            accountService.saveGlobalPermission(
                    PermissionTargetType.GROUP,
                    group.id(),
                    PermissionInput(role)
            )
            group
        }
    }

    protected fun  setProperty(projectEntity: ProjectEntity, propertyTypeClass: Class>, data: T) {
        asUser().with(projectEntity, ProjectEdit::class.java).execute(Runnable {
            propertyService.editProperty(
                    projectEntity,
                    propertyTypeClass,
                    data
            )
        }
        )
    }

    protected fun  deleteProperty(projectEntity: ProjectEntity?, propertyTypeClass: Class>) {
        asUser().with(projectEntity!!, ProjectEdit::class.java).execute(Runnable {
            propertyService.deleteProperty(
                    projectEntity,
                    propertyTypeClass
            )
        }
        )
    }

    protected fun  getProperty(projectEntity: ProjectEntity, propertyTypeClass: Class>): T {
        return asUser().with(projectEntity, ProjectEdit::class.java).call {
            propertyService.getProperty(
                    projectEntity,
                    propertyTypeClass
            ).value
        }
    }

    @JvmOverloads
    protected fun doCreateProject(nameDescription: NameDescription = nameDescription()): Project {
        return asUser().with(ProjectCreation::class.java).call {
            structureService.newProject(
                    of(nameDescription)
            )
        }
    }

    @JvmOverloads
    protected fun doCreateBranch(project: Project = doCreateProject(), nameDescription: NameDescription = nameDescription()): Branch {
        return asUser().with(project.id(), BranchCreate::class.java).call {
            structureService.newBranch(
                    of(project, nameDescription)
            )
        }
    }

    @JvmOverloads
    protected fun doCreateBuild(branch: Branch = doCreateBranch(), nameDescription: NameDescription = nameDescription(), signature: Signature = of("test")): Build {
        return asUser().with(branch.projectId(), BuildCreate::class.java).call {
            structureService.newBuild(
                    of(
                            branch,
                            nameDescription,
                            signature
                    )
            )
        }
    }

    @JvmOverloads
    fun doValidateBuild(
            build: Build,
            vs: ValidationStamp,
            statusId: ValidationRunStatusID?,
            runData: ValidationRunData<*>? = null
    ): ValidationRun {
        return asUser().withView(build).with(build, ValidationRunCreate::class.java).call {
            structureService.newValidationRun(
                    build,
                    ValidationRunRequest(
                            vs.name,
                            statusId,
                            runData?.descriptor?.id,
                            runData?.data,
                            null
                    )
            )
        }
    }

    fun doValidateBuild(build: Build, vsName: String, statusId: ValidationRunStatusID): ValidationRun {
        val vs = doCreateValidationStamp(build.branch, nd(vsName, ""))
        return doValidateBuild(build, vs, statusId)
    }

    @JvmOverloads
    protected fun doCreatePromotionLevel(branch: Branch = doCreateBranch(), nameDescription: NameDescription = nameDescription()): PromotionLevel {
        return asUser().with(branch.projectId(), PromotionLevelCreate::class.java).call {
            structureService.newPromotionLevel(
                    PromotionLevel.of(
                            branch,
                            nameDescription
                    )
            )
        }
    }

    protected fun doCreateValidationStamp(): ValidationStamp {
        return doCreateValidationStamp(doCreateBranch(), nameDescription())
    }

    protected fun doCreateValidationStamp(config: ValidationDataTypeConfig<*>?): ValidationStamp {
        return doCreateValidationStamp(doCreateBranch(), nameDescription(), config)
    }

    @JvmOverloads
    fun doCreateValidationStamp(branch: Branch, nameDescription: NameDescription, config: ValidationDataTypeConfig<*>? = null): ValidationStamp {
        return asUser().with(branch.project.id(), ValidationStampCreate::class.java).call {
            structureService.newValidationStamp(
                    ValidationStamp.of(
                            branch,
                            nameDescription
                    ).withDataType(config)
            )
        }
    }

    @JvmOverloads
    protected fun doPromote(build: Build, promotionLevel: PromotionLevel, description: String?, signature: Signature = of("test")): PromotionRun {
        return asUser().with(build.projectId(), PromotionRunCreate::class.java).call {
            structureService.newPromotionRun(
                    of(
                            build,
                            promotionLevel,
                            signature,
                            description
                    )
            )
        }
    }

    protected fun  doSetProperty(entity: ProjectEntity, propertyType: Class>, data: T) {
        asUser().with(entity, ProjectEdit::class.java).call {
            propertyService.editProperty(
                    entity,
                    propertyType,
                    data
            )
        }
    }

    protected fun asUser(): UserCall = UserCall()

    protected fun asAdmin(): AdminCall = AdminCall()

    protected fun asAnonymous(): AnonymousCall {
        return AnonymousCall()
    }

    protected fun asUserWithView(vararg entities: ProjectEntity): ConfigurableAccountCall {
        var user: ConfigurableAccountCall = asUser()
        for (entity in entities) {
            user = user.withView(entity)
        }
        return user
    }

    protected fun asFixedAccount(account: Account): AccountCall<*> {
        return FixedAccountCall(account)
    }

    protected fun  asFixedAccount(account: Account, code: () -> T): T = asFixedAccount(account).call(code)

    protected fun asConfigurableAccount(account: Account): ConfigurableAccountCall {
        return ConfigurableAccountCall(account)
    }

    protected fun asGlobalRole(role: String): AccountCall<*> {
        return FixedAccountCall(doCreateAccountWithGlobalRole(role))
    }

    protected fun  asGlobalRole(role: String, code: () -> T): T = asGlobalRole(role).call(code)

    protected fun  view(projectEntity: ProjectEntity, callable: Callable): T {
        return asUser().with(projectEntity.projectId(), ProjectView::class.java).call { callable.call() }
    }

    /**
     * This must always be called from [withGrantViewToAll] or [withNoGrantViewToAll].
     */
    private fun securitySettings(settings: SecuritySettings): SecuritySettings = asUser().with(GlobalSettings::class.java).call {
        val old = cachedSettingsService.getCachedSettings(SecuritySettings::class.java)
        settingsManagerService.saveSettings(settings)
        old
    }

    private fun  withSettings(grantViewToAll: Boolean, grantParticipationToAll: Boolean = true, task: () -> T): T {
        val old = securitySettings(SecuritySettings(
                isGrantProjectViewToAll = grantViewToAll,
                isGrantProjectParticipationToAll = grantParticipationToAll
        ))
        return try {
            task()
        } finally {
            securitySettings(old)
        }
    }

    protected fun  withGrantViewToAll(task: () -> T): T = withSettings(
            grantViewToAll = true,
            grantParticipationToAll = true,
            task = task
    )

    protected fun  withGrantViewAndNOParticipationToAll(task: () -> T): T = withSettings(
            grantViewToAll = true,
            grantParticipationToAll = false,
            task = task
    )

    protected fun  withNoGrantViewToAll(task: () -> T): T = withSettings(
            grantViewToAll = false,
            grantParticipationToAll = true,
            task = task
    )

    protected interface ContextCall {
        fun  call(call: () -> T): T
    }

    protected abstract class AbstractContextCall : ContextCall {

        override fun  call(call: () -> T): T {
            // Gets the current context
            val oldContext = SecurityContextHolder.getContext()
            return try {
                // Sets the new context
                contextSetup()
                // Call
                call()
            } finally {
                // Restores the context
                SecurityContextHolder.setContext(oldContext)
            }
        }

        fun execute(task: () -> Unit) = call(task)

        fun execute(task: Runnable) {
            call {
                task.run()
            }
        }

        protected abstract fun contextSetup()
    }

    protected class AnonymousCall : AbstractContextCall() {
        override fun contextSetup() {
            val context: SecurityContext = SecurityContextImpl()
            context.authentication = null
            SecurityContextHolder.setContext(context)
        }
    }

    protected open inner class AccountCall>(
            protected val account: Account
    ) : AbstractContextCall() {

        override fun contextSetup() {
            val context: SecurityContext = SecurityContextImpl()
            val ontrackAuthenticatedUser = createOntrackAuthenticatedUser()
            val authentication = TestingAuthenticationToken(
                    ontrackAuthenticatedUser,
                    "",
                    account.role.name
            )
            context.authentication = authentication
            SecurityContextHolder.setContext(context)
        }

        protected open fun createOntrackAuthenticatedUser(): OntrackAuthenticatedUser =
                accountService.withACL(TestOntrackUser(account))

    }

    protected inner class FixedAccountCall(account: Account) : AccountCall(account)

    protected open inner class ConfigurableAccountCall(
            account: Account
    ) : AccountCall(account) {

        /**
         * Global function associated to any global role to create
         */
        private val globalFunctions = mutableSetOf>()

        /**
         * Project function associated to any project role to create
         */
        private val projectFunctions = mutableMapOf>>()

        /**
         * Associates a list of global functions to this account
         */
        @SafeVarargs
        fun with(vararg fn: Class): ConfigurableAccountCall {
            globalFunctions.addAll(fn)
            return this
        }

        /**
         * Associates a list of project functions for a given project to this account
         */
        fun with(projectId: Int, fn: Class): ConfigurableAccountCall {
            val projectFns = projectFunctions.getOrPut(projectId) { mutableSetOf() }
            projectFns.add(fn)
            return this
        }

        /**
         * Associates a list of project functions for a given project (designated by the [entity][e]) to this account
         */
        fun with(e: ProjectEntity, fn: Class): ConfigurableAccountCall {
            return with(e.projectId(), fn)
        }

        /**
         * Grants the [ProjectView] function to this account and the project designated by the [entity][e].
         */
        fun withView(e: ProjectEntity): ConfigurableAccountCall {
            return with(e, ProjectView::class.java)
        }

        override fun contextSetup() {
            val context: SecurityContext = SecurityContextImpl()
            val ontrackAuthenticatedUser = createOntrackAuthenticatedUser()
            val authentication = TestingAuthenticationToken(
                    ontrackAuthenticatedUser,
                    "",
                    account.role.name
            )
            context.authentication = authentication
            SecurityContextHolder.setContext(context)
        }

        override fun createOntrackAuthenticatedUser(): OntrackAuthenticatedUser {
            // Configures the account
            securityService.asAdmin {
                // Creating a global role if some global functions are required
                if (globalFunctions.isNotEmpty()) {
                    val globalRoleId = uid("GR")
                    rolesService.registerGlobalRole(
                            id = globalRoleId,
                            name = "Test role $globalRoleId",
                            description = "Test role $globalRoleId",
                            globalFunctions = globalFunctions.toList(),
                            projectFunctions = emptyList()
                    )
                    accountService.saveGlobalPermission(
                            PermissionTargetType.ACCOUNT,
                            account.id(),
                            PermissionInput(globalRoleId)
                    )
                }
                // Project permissions
                projectFunctions.forEach { (projectId, functions) ->
                    if (functions.isNotEmpty()) {
                        val projectRoleId = uid("PR")
                        rolesService.registerProjectRole(
                                id = projectRoleId,
                                name = "Test role $projectRoleId",
                                description = "Test role $projectRoleId",
                                projectFunctions = functions.toList()
                        )
                        accountService.saveProjectPermission(
                                of(projectId),
                                PermissionTargetType.ACCOUNT,
                                account.id(),
                                PermissionInput(projectRoleId)
                        )
                    }
                }
            }
            // Loading the account
            return super.createOntrackAuthenticatedUser()
        }
    }

    protected inner class UserCall : ConfigurableAccountCall(
            securityService.asAdmin {
                val name = uid("U")
                accountService.create(
                        AccountInput(
                                name,
                                "$name von Test",
                                "[email protected]",
                                "xxx",
                                emptyList()
                        )
                )
            }
    )

    protected inner class AdminCall : AccountCall(
            // Loading the predefined admin account
            securityService.asAdmin {
                accountService.getAccount(of(1))
            }
    )
}

private class TestOntrackUser(
        private val account: Account
) : OntrackUser {

    override val accountId: Int = account.id()

    override fun getAuthorities(): Collection =
            AuthorityUtils.createAuthorityList(account.role.roleName)

    override fun isEnabled(): Boolean = true

    override fun getUsername(): String = account.name

    override fun isCredentialsNonExpired(): Boolean = true

    override fun getPassword(): String = ""

    override fun isAccountNonExpired(): Boolean = true

    override fun isAccountNonLocked(): Boolean = true

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy