Skip to content

Commit

Permalink
Merge pull request #138 from Paul-Blanchaert/enable-pac4j-basicauth
Browse files Browse the repository at this point in the history
Enable basic auth from configured user/password
  • Loading branch information
epugh authored Feb 20, 2024
2 parents 8c2b789 + 8e8f238 commit 531b182
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 10 deletions.
30 changes: 30 additions & 0 deletions app/modules/ConfiguredBasicAuthAuthenticator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package modules

import org.pac4j.core.context.WebContext
import org.pac4j.core.context.session.SessionStore
import org.pac4j.core.credentials.{Credentials, UsernamePasswordCredentials}
import org.pac4j.core.credentials.authenticator.Authenticator
import org.pac4j.core.exception.CredentialsException
import org.pac4j.core.profile.CommonProfile
import org.pac4j.core.util.{CommonHelper, Pac4jConstants}


case class ConfiguredBasicAuthAuthenticator(validUserId: String, validPassword: String) extends Authenticator {

override def validate(credentials: Credentials, context: WebContext, sessionStore: SessionStore): Unit = {
if (credentials == null) throw new CredentialsException("No credential")
val userCredentials = credentials.asInstanceOf[UsernamePasswordCredentials]
val username = userCredentials.getUsername()
val password = userCredentials.getPassword()
if (CommonHelper.isBlank(username)) throw new CredentialsException("Username cannot be blank")
if (CommonHelper.isBlank(password)) throw new CredentialsException("Password cannot be blank")
if (CommonHelper.areNotEquals(username, validUserId)) throw new CredentialsException("Username : '" + username + "' does not match valid user")
if (CommonHelper.areNotEquals(password, validPassword)) throw new CredentialsException("Password does not match valid password")
val profile = new CommonProfile
profile.setId(username)
profile.addAttribute(Pac4jConstants.USERNAME, username)
userCredentials.setUserProfile(profile)
}

}

27 changes: 18 additions & 9 deletions app/modules/SecurityModule.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package modules

import com.google.inject.{AbstractModule, Provides}
import org.pac4j.core.client.Clients
import org.pac4j.core.client.{Client, Clients}
import org.pac4j.core.client.direct.AnonymousClient
import org.pac4j.core.config.Config
import org.pac4j.core.context.session.SessionStore
import org.pac4j.core.profile.CommonProfile
import org.pac4j.http.client.direct.DirectBasicAuthClient
import org.pac4j.play.scala.{DefaultSecurityComponents, Pac4jScalaTemplateHelper, SecurityComponents}
import org.pac4j.play.store.{PlayCookieSessionStore, ShiroAesDataEncrypter}
import org.pac4j.play.{CallbackController, LogoutController}
Expand Down Expand Up @@ -43,16 +44,21 @@ class SecurityModule(environment: Environment, configuration: Configuration) ext
@Provides
def provideConfig(): Config = {
val maybeConfiguredClientName = configuration.getOptional[String](ConfigKeyAuthClient).filter(_.nonEmpty)
val authClientOpt = maybeConfiguredClientName.map {
case "SAML2Client" => createSaml2Client(s"$ConfigKeyPrefixClientConfig.SAML2Client")
val config: Option[Config] = maybeConfiguredClientName.map {
case "DirectBasicAuthClient" => createConfiguredDirectBasicAuthConfig(s"$ConfigKeyPrefixClientConfig.ConfiguredDirectBasicAuthClient")
case "SAML2Client" => createSaml2Config(s"$ConfigKeyPrefixClientConfig.SAML2Client")
case other => throw new RuntimeException(s"Unsupported auth client config value: $other")
}
val allClients = authClientOpt.toSeq :+ new AnonymousClient()
// callback URL path as configured in `routes`
val clients = new Clients(s"$baseUrl/callback", allClients:_*)
new Config(clients)
config.getOrElse(new Config())
}

private def createConfiguredDirectBasicAuthConfig(keyPrefix: String): Config = {
val username = configuration.get[String](s"$keyPrefix.username")
val password = configuration.get[String](s"$keyPrefix.password")
new Config(new DirectBasicAuthClient(ConfiguredBasicAuthAuthenticator(username, password)))
}
private def createSaml2Client(keyPrefix: String): SAML2Client = {

private def createSaml2Config(keyPrefix: String): Config = {
val cfg = new SAML2Configuration(
configuration.get[String](s"$keyPrefix.keystore"),
configuration.get[String](s"$keyPrefix.keystorePassword"),
Expand All @@ -62,7 +68,10 @@ class SecurityModule(environment: Environment, configuration: Configuration) ext
cfg.setServiceProviderEntityId(configuration.get[String](s"$keyPrefix.serviceProviderEntityId"))
cfg.setServiceProviderMetadataPath(configuration.get[String](s"$keyPrefix.serviceProviderMetadataPath"))
cfg.setMaximumAuthenticationLifetime(configuration.get[Long](s"$keyPrefix.maximumAuthenticationLifetime"))
new SAML2Client(cfg)
val allClients = Option(new SAML2Client(cfg)).toSeq :+ new AnonymousClient()
// callback URL path as configured in `routes`
val clients = new Clients(s"$baseUrl/callback", allClients:_*)
new Config(clients)
}

}
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import com.typesafe.sbt.GitBranchPrompt

name := "search-management-ui"
version := "4.0.6"
version := "4.0.7"
maintainer := "Contact productful.io <hello@productful.io>"

scalaVersion := "2.12.17"
Expand Down
8 changes: 8 additions & 0 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ play.http.session.maxAge = ${?SMUI_SESSION_MAXAGE}

# This defines the pac4j client to use for authentication. Uses the well-defined pac4j names (OidcClient,
# DirectBasicAuthClient...), currently supported:
# - DirectBasicAuthClient
# - SAML2Client
# If no or empty value is set, no authentication is used.
smui.auth.client = ""
Expand Down Expand Up @@ -218,6 +219,13 @@ smui.auth.clients.SAML2Client {
maximumAuthenticationLifetime = ${?SMUI_SAML_MAXIMUM_AUTHENTICATION_LIFETIME}
}

# The DirectBasicAuthClient configuration which is used *only* when the DirectBasicAuthClient is configured as smui.auth.client
smui.auth.clients.ConfiguredDirectBasicAuthClient {
username = "smui_user"
username = ${?SMUI_BASIC_AUTH_PASS}
password = "smui_pass"
password = ${?SMUI_BASIC_AUTH_PASS}
}

# pac4j rules for configuring the pac4j SecurityFilter
pac4j.security.rules = [
Expand Down

0 comments on commit 531b182

Please sign in to comment.