Skip to content

Commit

Permalink
Merge pull request #132 from querqy/smui_saml
Browse files Browse the repository at this point in the history
Smui saml
  • Loading branch information
pbartusch authored Oct 23, 2023
2 parents de13877 + 41a7a85 commit 886b343
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 549 deletions.
97 changes: 52 additions & 45 deletions app/controllers/ApiController.scala

Large diffs are not rendered by default.

28 changes: 12 additions & 16 deletions app/controllers/FrontendController.scala
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
package controllers

import javax.inject.Inject
import controllers.auth.AuthActionFactory
import play.api.{Configuration, Logging}
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}
import models.FeatureToggleModel._
import javax.inject._
import play.api.Configuration
import org.pac4j.core.profile.UserProfile
import org.pac4j.play.scala.{Security, SecurityComponents}
import play.api.Logging
import play.api.http.HttpErrorHandler
import play.api.libs.json.Json
import play.api.mvc._

class FrontendController @Inject()(cc: MessagesControllerComponents,
import javax.inject.Inject
import scala.concurrent.ExecutionContext

class FrontendController @Inject()(val controllerComponents: SecurityComponents,
assets: Assets,
errorHandler: HttpErrorHandler,
authActionFactory: AuthActionFactory)(implicit executionContext: ExecutionContext)
extends MessagesAbstractController(cc) with Logging {
errorHandler: HttpErrorHandler)
(implicit executionContext: ExecutionContext)
extends Security [UserProfile] with play.api.i18n.I18nSupport with Logging {

def index(): Action[AnyContent] = authActionFactory.getAuthenticatedAction(Action).async { request =>
def index(): Action[AnyContent] = Action.async { request =>
assets.at("index.html")(request)
}

def assetOrDefault(resource: String): Action[AnyContent] = authActionFactory.getAuthenticatedAction(Action).async { request =>
def assetOrDefault(resource: String): Action[AnyContent] = Action.async { request =>
if (resource.startsWith("api")) {
errorHandler.onClientError(request, NOT_FOUND, "Not found")
} else {
Expand Down
49 changes: 0 additions & 49 deletions app/controllers/auth/AuthActionFactory.scala

This file was deleted.

72 changes: 0 additions & 72 deletions app/controllers/auth/BasicAuthAuthenticatedAction.scala

This file was deleted.

96 changes: 0 additions & 96 deletions app/controllers/auth/JWTJsonAuthenticatedAction.scala

This file was deleted.

23 changes: 23 additions & 0 deletions app/filters/Filters.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package filters

import modules.SecurityModule
import org.pac4j.play.filters.SecurityFilter
import play.api.Configuration
import play.api.http.HttpFilters
import play.api.mvc.EssentialFilter

import javax.inject.Inject

class Filters @Inject()(configuration: Configuration, securityFilter: SecurityFilter) extends HttpFilters {

override def filters: Seq[EssentialFilter] = {
// The securityFilter applies the pac4j security rules. It is only used if a non-empty authentication
// client has been configured.
if (configuration.getOptional[String](SecurityModule.ConfigKeyAuthClient).exists(_.nonEmpty)) {
Seq(securityFilter)
} else {
Seq.empty
}
}

}
3 changes: 0 additions & 3 deletions app/models/FeatureToggleModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ package object FeatureToggleModel extends Logging {
private val FEATURE_TOGGLE_HEADLINE = "toggle.headline"
private val ACTIVATE_RULE_TAGGING = "toggle.rule-tagging"
private val PREDEFINED_TAGS_FILE = "toggle.predefined-tags-file"
private val SMUI_AUTH_SIMPLE_LOGOUT = "smui.auth.ui-concept.simple-logout-button-target-url"
private val SMUI_VERSION = "smui.version"
private val FEATURE_TOGGLE_ACTIVATE_SPELLING = "toggle.activate-spelling"
private val SMUI_DEFAULT_DISPLAY_USERNAME = "toggle.display-username.default"
Expand Down Expand Up @@ -137,8 +136,6 @@ package object FeatureToggleModel extends Logging {
jsBoolFeatureToggle(ACTIVATE_RULE_TAGGING, false),
JsFeatureToggle(FEATURE_TOGGLE_HEADLINE, new JsStringFeatureToggleValue(
appConfig.getOptional[String](FEATURE_TOGGLE_HEADLINE).getOrElse("Search Management UI"))),
JsFeatureToggle(SMUI_AUTH_SIMPLE_LOGOUT, new JsStringFeatureToggleValue(
appConfig.getOptional[String](SMUI_AUTH_SIMPLE_LOGOUT).getOrElse(""))),
JsFeatureToggle(SMUI_VERSION, new JsStringFeatureToggleValue(models.buildInfo.BuildInfo.version)),
JsFeatureToggle(FEATURE_TOGGLE_UI_LIST_LIMIT_ITEMS_TO, new JsStringFeatureToggleValue(
appConfig.getOptional[String](FEATURE_TOGGLE_UI_LIST_LIMIT_ITEMS_TO).getOrElse("-1"))),
Expand Down
73 changes: 73 additions & 0 deletions app/modules/SecurityModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package modules

import com.google.inject.{AbstractModule, Provides}
import org.pac4j.core.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.play.scala.{DefaultSecurityComponents, Pac4jScalaTemplateHelper, SecurityComponents}
import org.pac4j.play.store.{PlayCookieSessionStore, ShiroAesDataEncrypter}
import org.pac4j.play.{CallbackController, LogoutController}
import org.pac4j.saml.client.SAML2Client
import org.pac4j.saml.config.SAML2Configuration
import play.api.{Configuration, Environment}

import java.nio.charset.StandardCharsets

class SecurityModule(environment: Environment, configuration: Configuration) extends AbstractModule {

import SecurityModule._

private val baseUrl = configuration.get[String]("smui.auth.baseUrl")

override def configure(): Unit = {
val sKey = configuration.get[String]("play.http.secret.key").substring(0, 16)
val dataEncrypter = new ShiroAesDataEncrypter(sKey.getBytes(StandardCharsets.UTF_8))
val playSessionStore = new PlayCookieSessionStore(dataEncrypter)
bind(classOf[SessionStore]).toInstance(playSessionStore)
bind(classOf[SecurityComponents]).to(classOf[DefaultSecurityComponents])
bind(classOf[Pac4jScalaTemplateHelper[CommonProfile]])

// callback
val callbackController = new CallbackController()
callbackController.setDefaultUrl("/")
bind(classOf[CallbackController]).toInstance(callbackController)

// logout
val logoutController = new LogoutController()
logoutController.setDefaultUrl("/")
bind(classOf[LogoutController]).toInstance(logoutController)
}

@Provides
def provideConfig(): Config = {
val maybeConfiguredClientName = configuration.getOptional[String](ConfigKeyAuthClient).filter(_.nonEmpty)
val authClientOpt = maybeConfiguredClientName.map {
case "SAML2Client" => createSaml2Client(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)
}
private def createSaml2Client(keyPrefix: String): SAML2Client = {
val cfg = new SAML2Configuration(
configuration.get[String](s"$keyPrefix.keystore"),
configuration.get[String](s"$keyPrefix.keystorePassword"),
configuration.get[String](s"$keyPrefix.privateKeyPassword"),
configuration.get[String](s"$keyPrefix.identityProviderMetadataPath")
)
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)
}

}

object SecurityModule {
val ConfigKeyAuthClient = "smui.auth.client"
val ConfigKeyPrefixClientConfig = "smui.auth.clients"
}
Loading

0 comments on commit 886b343

Please sign in to comment.