Skip to content

Commit

Permalink
Merge pull request #5408 from clarktsiory/bug_24230/authentication_pr…
Browse files Browse the repository at this point in the history
…oviders_and_role_mapping_settings_should_be_exposed

Fixes #24230: Authentication providers and role mapping settings should be exposed
  • Loading branch information
fanf committed Feb 29, 2024
2 parents e01d327 + d741fc7 commit ed0a64c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,13 @@ class JdbcUserRepository(doobie: Doobie) extends UserRepository {
fr")"

def update(updated: Vector[UserInfo]): ConnectionIO[Int] = {
// never update the user personal information and managedby of an existing user which may have been provided and modified by another origin
val sql =
"""insert into users (id, creationdate, status, managedby, name, email, lastlogin, statushistory, otherinfo)
values (?,?,?,?,? , ?,?,?,?)
on conflict (id) do update
set (creationdate, status, managedby, name, email, lastlogin, statushistory, otherinfo) =
(EXCLUDED.creationdate, EXCLUDED.status, EXCLUDED.managedby, EXCLUDED.name, EXCLUDED.email, EXCLUDED.lastlogin, EXCLUDED.statushistory, EXCLUDED.otherinfo)"""
set (creationdate, status, lastlogin, statushistory) =
(EXCLUDED.creationdate, EXCLUDED.status, EXCLUDED.lastlogin, EXCLUDED.statushistory)"""

Update[UserInfo](sql).updateMany(updated)
}
Expand Down Expand Up @@ -800,7 +801,7 @@ class JdbcUserRepository(doobie: Doobie) extends UserRepository {

// only disabled user can be set back to active
override def setActive(userId: List[String], trace: EventTrace): IOResult[Unit] = {
changeStatus(userId, None, Nil, trace, UserStatus.Active, Some(fr"status = '${UserStatus.Disabled.value}'")).unit
changeStatus(userId, None, Nil, trace, UserStatus.Active, Some(fr"status = ${UserStatus.Disabled.value}")).unit
}

override def getAll(): IOResult[List[UserInfo]] = {
Expand Down Expand Up @@ -831,7 +832,7 @@ class JdbcUserRepository(doobie: Doobie) extends UserRepository {
params match {
case Nil => ZIO.unit
case h :: tail =>
val sql = sql"""update users""" ++ Fragments.set(params: _*) ++ fr"""where id = ${id}"""
val sql = fr"""update users""" ++ Fragments.set(params: _*) ++ fr"""where id = ${id}"""

transactIOResult(s"Error when updating user information for '${id}'")(xa => sql.update.run.transact(xa)).unit
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.authentication.AuthenticationFailureHandler
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
import scala.annotation.nowarn
import scala.util.Try
import zio.syntax._

/**
Expand All @@ -110,6 +111,10 @@ class AppConfigAuth extends ApplicationContextAware {
// we define the System ApiAcl as one that is all mighty and can manage everything
val SYSTEM_API_ACL = ApiAuthorization.RW

// Configuration values within an authentication provider config from which we get provider properties
val A_ROLES_ENABLED = "roles.enabled"
val A_ROLES_OVERRIDE = "roles.override"

/*
* This method is expected to try to initialize the Spring bean for
* all authentication provider configured by the user for \rudder.auth.provider`.
Expand Down Expand Up @@ -137,7 +142,7 @@ class AppConfigAuth extends ApplicationContextAware {
// prepare specific properties for each configuredAuthProviders - we need system properties for spring

import scala.jdk.CollectionConverters._
configuredAuthProviders.foreach { x =>
val providerProperties: Map[String, AuthBackendProviderProperties] = configuredAuthProviders.flatMap { x =>
try {
// try to load all the specific properties of that auth type
// so that they are available from Spring
Expand All @@ -150,7 +155,30 @@ class AppConfigAuth extends ApplicationContextAware {
} catch {
case ex: ConfigException.Missing => // does nothing - the beauty of imperative prog :(
}
}
val properties: Option[(String, AuthBackendProviderProperties)] = x.name match {
case "ldap" =>
// roles for LDAP-provided users come directly from the file, it neither extends nor overrides the default roles
Some("ldap" -> AuthBackendProviderProperties("ldap", false, false, true))
case "oidc" | "oauth2" => {
val baseProperty = "rudder.auth.oauth2.provider"
// we need to read under the registration key, under the base property
val registrations =
Try(config.getString(baseProperty + ".registrations").split(",").map(_.trim).toList).getOrElse(List.empty)
// when there are multiple registrations we should combine properties because under the same provider, one registration may have some overrides
Some(x.name -> registrations.foldLeft(AuthBackendProviderProperties.empty) {
case (acc, reg) =>
val rolesEnabled = Try(config.getBoolean(s"${baseProperty}.${reg}.${A_ROLES_ENABLED}"))
.getOrElse(false) // default value, same as in the auth backend plugin, but also identity element of the monoid
val rolesOverride = Try(config.getBoolean(s"${baseProperty}.${reg}.${A_ROLES_OVERRIDE}"))
.getOrElse(false) // default value, same as in the auth backend plugin, but also identity element of the monoid
AuthBackendProviderProperties
.combine(x.name, acc, AuthBackendProviderProperties(x.name, rolesEnabled, rolesOverride, false))
})
}
case _ => None // default providers or providers for which handling the properties is still unkown
}
properties
}.toMap

// load additional beans from authentication dedicated resource files

Expand Down Expand Up @@ -191,6 +219,7 @@ class AppConfigAuth extends ApplicationContextAware {
}
}
RudderConfig.authenticationProviders.setConfiguredProviders(configuredAuthProviders.toArray)
RudderConfig.authenticationProviders.addProviderProperties(providerProperties)
}

///////////// FOR WEB INTERFACE /////////////
Expand Down Expand Up @@ -418,6 +447,55 @@ class RudderXmlUserDetailsContextMapper(authConfigProvider: UserDetailListProvid
}
}

/**
* A description of some properties of an authentication backend
* @param hasAdditionalRoles if the backend can provide additional roles than the ones of the default backend
* @param overridesRoles if the backend is configured to override the roles of the default backend and cannot change them
* @param hasModifiablePassword if the backend is configured to allow password modification
*/
final case class AuthBackendProviderProperties private (
hasAdditionalRoles: Boolean,
overridesRoles: Boolean,
hasModifiablePassword: Boolean
) {
def extendsRoles: Boolean = hasAdditionalRoles && !overridesRoles
}

object AuthBackendProviderProperties {
def apply(
name: String,
hasAdditionalRoles: Boolean,
overridesRoles: Boolean,
hasModifiablePassword: Boolean
): AuthBackendProviderProperties = {
if (overridesRoles && !hasAdditionalRoles) {
// This is not consistent so we force overridesRoles to false
ApplicationLogger.error(
s"Backend provider properties for '${name}' are not consistent: backend does not enables providing roles but roles overrides is set to true. " +
s"Overriding roles will not be enabled."
)
new AuthBackendProviderProperties(false, false, hasModifiablePassword)
} else {
new AuthBackendProviderProperties(hasAdditionalRoles, overridesRoles, hasModifiablePassword)
}
}

// properties set to false by default regarding roles is the default behavior, it may break all the properties parsing logic in this file otherwise
def empty: AuthBackendProviderProperties = new AuthBackendProviderProperties(false, false, false)
def combine(
name: String,
left: AuthBackendProviderProperties,
right: AuthBackendProviderProperties
): AuthBackendProviderProperties = {
AuthBackendProviderProperties(
name,
left.hasAdditionalRoles || right.hasAdditionalRoles,
left.overridesRoles || right.overridesRoles,
left.hasModifiablePassword || right.hasModifiablePassword
)
}
}

/**
* This is the class that defines the authentication methods.
* Without the plugin, by default only "file" is known.
Expand Down Expand Up @@ -500,6 +578,9 @@ class AuthBackendProvidersManager() extends DynamicRudderProviderManager {
// this is the map of configured spring bean "AuthenticationProvider"
private[this] var springProviders = Map[String, AuthenticationProvider]()

// a map of properties registered for each backend
private[this] var backendProperties = Map[String, AuthBackendProviderProperties]()

def addProvider(p: AuthBackendsProvider): Unit = {
ApplicationLogger.info(s"Add backend providers '${p.name}'")
backends = backends :+ p
Expand Down Expand Up @@ -528,6 +609,14 @@ class AuthBackendProvidersManager() extends DynamicRudderProviderManager {
this.authenticationMethods.toSeq
}

def addProviderProperties(properties: Map[String, AuthBackendProviderProperties]): Unit = {
this.backendProperties = this.backendProperties ++ properties
}

def getProviderProperties(): Map[String, AuthBackendProviderProperties] = {
this.backendProperties
}

/*
* what we
*/
Expand Down

0 comments on commit ed0a64c

Please sign in to comment.