Skip to content

Commit

Permalink
[DL-718] chore: Delete pki authent
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreLM3 committed Oct 10, 2024
1 parent 8840637 commit 1942ed8
Show file tree
Hide file tree
Showing 4 changed files with 7 additions and 184 deletions.
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ libraryDependencies ++= Seq(
"com.sksamuel.elastic4s" %% "elastic4s-client-esjava" % elastic4sVersion,
"com.typesafe.akka" %% "akka-stream-testkit" % play.core.PlayVersion.akkaVersion % Test,
"org.scalactic" %% "scalactic" % "3.2.19",
"org.bouncycastle" % "bcprov-jdk15on" % "1.70",
specs2 % Test
)

Expand Down

This file was deleted.

107 changes: 7 additions & 100 deletions elastic4play/app/org/elastic4play/controllers/Authenticated.scala
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
package org.elastic4play.controllers

import java.io.ByteArrayInputStream
import org.elastic4play.services.{AuthContext, AuthSrv, Role, UserSrv}
import org.elastic4play.utils.Instance
import org.elastic4play.{AuthenticationError, AuthorizationError}
import play.api.http.HeaderNames
import play.api.mvc._
import play.api.{Configuration, Logger}

import java.util.Date
import javax.inject.{Inject, Singleton}
import javax.naming.ldap.LdapName

import scala.concurrent.duration.{DurationLong, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future}
import scala.jdk.CollectionConverters._
import scala.util.Try

import play.api.{Configuration, Logger}
import play.api.http.HeaderNames
import play.api.mvc._

import org.bouncycastle.asn1._

import org.elastic4play.{AuthenticationError, AuthorizationError}
import org.elastic4play.services.{AuthContext, AuthSrv, Role, UserSrv}
import org.elastic4play.utils.Instance
import java.util.{List => JList}

/**
* A request with authentication information
*/
Expand All @@ -45,13 +37,11 @@ class Authenticated(
maxSessionInactivity: FiniteDuration,
sessionWarning: FiniteDuration,
sessionUsername: String,
certificateField: Option[String],
authHeaderName: Option[String],
authBySessionCookie: Boolean,
authByKey: Boolean,
authByBasicAuth: Boolean,
authByInitialUser: Boolean,
authByPki: Boolean,
authByHeader: Boolean,
userSrv: UserSrv,
authSrv: AuthSrv,
Expand All @@ -64,19 +54,11 @@ class Authenticated(
configuration.getMillis("session.inactivity").millis,
configuration.getMillis("session.warning").millis,
configuration.getOptional[String]("session.username").getOrElse("username"),
configuration
.getOptional[String]("auth.pki.certificateField")
.map(_.toLowerCase)
.map {
case "userprincipalname" => "upn"
case f => f
},
configuration.getOptional[String]("auth.header.name"),
configuration.getOptional[Boolean]("auth.method.session").getOrElse(true),
configuration.getOptional[Boolean]("auth.method.key").getOrElse(true),
configuration.getOptional[Boolean]("auth.method.basic").getOrElse(true),
configuration.getOptional[Boolean]("auth.method.init").getOrElse(true),
configuration.getOptional[Boolean]("auth.method.pki").getOrElse(true),
configuration.getOptional[Boolean]("auth.method.header").getOrElse(false),
userSrv,
authSrv,
Expand Down Expand Up @@ -161,80 +143,6 @@ class Authenticated(
}
} yield authContext

private def asn1String(obj: ASN1Primitive): String = obj match {
case ds: DERUTF8String => DERUTF8String.getInstance(ds).getString
case to: ASN1TaggedObject => asn1String(ASN1TaggedObject.getInstance(to).getObject)
case os: ASN1OctetString => new String(os.getOctets)
case as: ASN1String => as.getString
}

private object CertificateSAN {

def unapply(l: JList[_]): Option[(String, String)] = {
val typeValue = for {
t <- Option(l.get(0))
v <- Option(l.get(1))
} yield t -> v
typeValue
.collect { case (t: Integer, v) => t.toInt -> v }
.collect {
case (0, value: Array[Byte]) =>
val asn1 = new ASN1InputStream(new ByteArrayInputStream(value)).readObject()
val asn1Seq = ASN1Sequence.getInstance(asn1)
val id = ASN1ObjectIdentifier.getInstance(asn1Seq.getObjectAt(0)).getId
val valueStr = asn1String(asn1Seq.getObjectAt(1).toASN1Primitive)

id match {
case "1.3.6.1.4.1.311.20.2.3" => "upn" -> valueStr
// Add other object id
case other => other -> valueStr
}
case (1, value: String) => "rfc822Name" -> value
case (2, value: String) => "dNSName" -> value
case (3, value: String) => "x400Address" -> value
case (4, value: String) => "directoryName" -> value
case (5, value: String) => "ediPartyName" -> value
case (6, value: String) => "uniformResourceIdentifier" -> value
case (7, value: String) => "iPAddress" -> value
case (8, value: String) => "registeredID" -> value
}
}
}

def getFromClientCertificate(request: RequestHeader): Future[AuthContext] =
certificateField
.fold[Future[AuthContext]](Future.failed(AuthenticationError("Certificate authentication is not configured"))) { cf =>
logger.debug(s"Client certificate is : ${request.clientCertificateChain.toList.flatten.map(_.getSubjectX500Principal.getName).mkString(";")}")
request
.clientCertificateChain
.flatMap(_.headOption)
.flatMap { cert =>
val dn = cert.getSubjectX500Principal.getName
val ldapName = new LdapName(dn)
val rdns = ldapName.getRdns.asScala
logger.debug(s"Client certificate subject is ${rdns.map(x => x.getType + "=" + x.getValue.toString).mkString(",")}")
rdns
.collectFirst {
case rdn if rdn.getType.toLowerCase == cf =>
logger.debug(s"Found user id ${rdn.getValue} in dn:$cf")
userSrv.getFromId(request, rdn.getValue.toString.toLowerCase, "pki")
}
.orElse {
logger.debug(s"Field $cf not found in certificate subject")
for {
san <- Option(cert.getSubjectAlternativeNames)
_ = logger.debug(s"Subject alternative name is ${san.asScala.mkString(",")}")
fieldValue <- san.asScala.collectFirst {
case CertificateSAN(name, value) if name.toLowerCase == cf =>
logger.debug(s"Found user id $value in san:$cf")
userSrv.getFromId(request, value.toLowerCase, "pki")
}
} yield fieldValue
}
}
.getOrElse(Future.failed(AuthenticationError("Certificate doesn't contain user information")))
}

def getFromHeader(request: RequestHeader): Future[AuthContext] =
for {
header <- authHeaderName.fold[Future[String]](Future.failed(AuthenticationError("HTTP header is not configured")))(Future.successful)
Expand All @@ -244,7 +152,6 @@ class Authenticated(

val authenticationMethods: Seq[(String, RequestHeader => Future[AuthContext])] =
(if (authBySessionCookie) Seq("session" -> getFromSession _) else Nil) ++
(if (authByPki) Seq("pki" -> getFromClientCertificate _) else Nil) ++
(if (authByKey) Seq("key" -> getFromApiKey _) else Nil) ++
(if (authByBasicAuth) Seq("basic" -> getFromBasicAuth _) else Nil) ++
(if (authByInitialUser) Seq("init" -> userSrv.getInitialUser _) else Nil) ++
Expand Down
2 changes: 0 additions & 2 deletions elastic4play/conf/reference.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# handler for errors (transform exception to related http status code
play.http.errorHandler = org.elastic4play.ErrorHandler

# SSL engine used to ask client certificate
play.http.sslengineprovider=org.elastic4play.ClientAuthSSLEngineProvider

0 comments on commit 1942ed8

Please sign in to comment.