11package org .elastic4play .controllers
22
3- import java .io .ByteArrayInputStream
3+ import org .elastic4play .services .{AuthContext , AuthSrv , Role , UserSrv }
4+ import org .elastic4play .utils .Instance
5+ import org .elastic4play .{AuthenticationError , AuthorizationError }
6+ import play .api .http .HeaderNames
7+ import play .api .mvc ._
8+ import play .api .{Configuration , Logger }
9+
410import java .util .Date
511import javax .inject .{Inject , Singleton }
6- import javax .naming .ldap .LdapName
7-
812import scala .concurrent .duration .{DurationLong , FiniteDuration }
913import scala .concurrent .{ExecutionContext , Future }
10- import scala .jdk .CollectionConverters ._
1114import scala .util .Try
1215
13- import play .api .{Configuration , Logger }
14- import play .api .http .HeaderNames
15- import play .api .mvc ._
16-
17- import org .bouncycastle .asn1 ._
18-
19- import org .elastic4play .{AuthenticationError , AuthorizationError }
20- import org .elastic4play .services .{AuthContext , AuthSrv , Role , UserSrv }
21- import org .elastic4play .utils .Instance
22- import java .util .{List => JList }
23-
2416/**
2517 * A request with authentication information
2618 */
@@ -45,13 +37,11 @@ class Authenticated(
4537 maxSessionInactivity : FiniteDuration ,
4638 sessionWarning : FiniteDuration ,
4739 sessionUsername : String ,
48- certificateField : Option [String ],
4940 authHeaderName : Option [String ],
5041 authBySessionCookie : Boolean ,
5142 authByKey : Boolean ,
5243 authByBasicAuth : Boolean ,
5344 authByInitialUser : Boolean ,
54- authByPki : Boolean ,
5545 authByHeader : Boolean ,
5646 userSrv : UserSrv ,
5747 authSrv : AuthSrv ,
@@ -64,19 +54,11 @@ class Authenticated(
6454 configuration.getMillis(" session.inactivity" ).millis,
6555 configuration.getMillis(" session.warning" ).millis,
6656 configuration.getOptional[String ](" session.username" ).getOrElse(" username" ),
67- configuration
68- .getOptional[String ](" auth.pki.certificateField" )
69- .map(_.toLowerCase)
70- .map {
71- case " userprincipalname" => " upn"
72- case f => f
73- },
7457 configuration.getOptional[String ](" auth.header.name" ),
7558 configuration.getOptional[Boolean ](" auth.method.session" ).getOrElse(true ),
7659 configuration.getOptional[Boolean ](" auth.method.key" ).getOrElse(true ),
7760 configuration.getOptional[Boolean ](" auth.method.basic" ).getOrElse(true ),
7861 configuration.getOptional[Boolean ](" auth.method.init" ).getOrElse(true ),
79- configuration.getOptional[Boolean ](" auth.method.pki" ).getOrElse(true ),
8062 configuration.getOptional[Boolean ](" auth.method.header" ).getOrElse(false ),
8163 userSrv,
8264 authSrv,
@@ -161,80 +143,6 @@ class Authenticated(
161143 }
162144 } yield authContext
163145
164- private def asn1String (obj : ASN1Primitive ): String = obj match {
165- case ds : DERUTF8String => DERUTF8String .getInstance(ds).getString
166- case to : ASN1TaggedObject => asn1String(ASN1TaggedObject .getInstance(to).getObject)
167- case os : ASN1OctetString => new String (os.getOctets)
168- case as : ASN1String => as.getString
169- }
170-
171- private object CertificateSAN {
172-
173- def unapply (l : JList [_]): Option [(String , String )] = {
174- val typeValue = for {
175- t <- Option (l.get(0 ))
176- v <- Option (l.get(1 ))
177- } yield t -> v
178- typeValue
179- .collect { case (t : Integer , v) => t.toInt -> v }
180- .collect {
181- case (0 , value : Array [Byte ]) =>
182- val asn1 = new ASN1InputStream (new ByteArrayInputStream (value)).readObject()
183- val asn1Seq = ASN1Sequence .getInstance(asn1)
184- val id = ASN1ObjectIdentifier .getInstance(asn1Seq.getObjectAt(0 )).getId
185- val valueStr = asn1String(asn1Seq.getObjectAt(1 ).toASN1Primitive)
186-
187- id match {
188- case " 1.3.6.1.4.1.311.20.2.3" => " upn" -> valueStr
189- // Add other object id
190- case other => other -> valueStr
191- }
192- case (1 , value : String ) => " rfc822Name" -> value
193- case (2 , value : String ) => " dNSName" -> value
194- case (3 , value : String ) => " x400Address" -> value
195- case (4 , value : String ) => " directoryName" -> value
196- case (5 , value : String ) => " ediPartyName" -> value
197- case (6 , value : String ) => " uniformResourceIdentifier" -> value
198- case (7 , value : String ) => " iPAddress" -> value
199- case (8 , value : String ) => " registeredID" -> value
200- }
201- }
202- }
203-
204- def getFromClientCertificate (request : RequestHeader ): Future [AuthContext ] =
205- certificateField
206- .fold[Future [AuthContext ]](Future .failed(AuthenticationError (" Certificate authentication is not configured" ))) { cf =>
207- logger.debug(s " Client certificate is : ${request.clientCertificateChain.toList.flatten.map(_.getSubjectX500Principal.getName).mkString(" ;" )}" )
208- request
209- .clientCertificateChain
210- .flatMap(_.headOption)
211- .flatMap { cert =>
212- val dn = cert.getSubjectX500Principal.getName
213- val ldapName = new LdapName (dn)
214- val rdns = ldapName.getRdns.asScala
215- logger.debug(s " Client certificate subject is ${rdns.map(x => x.getType + " =" + x.getValue.toString).mkString(" ," )}" )
216- rdns
217- .collectFirst {
218- case rdn if rdn.getType.toLowerCase == cf =>
219- logger.debug(s " Found user id ${rdn.getValue} in dn: $cf" )
220- userSrv.getFromId(request, rdn.getValue.toString.toLowerCase, " pki" )
221- }
222- .orElse {
223- logger.debug(s " Field $cf not found in certificate subject " )
224- for {
225- san <- Option (cert.getSubjectAlternativeNames)
226- _ = logger.debug(s " Subject alternative name is ${san.asScala.mkString(" ," )}" )
227- fieldValue <- san.asScala.collectFirst {
228- case CertificateSAN (name, value) if name.toLowerCase == cf =>
229- logger.debug(s " Found user id $value in san: $cf" )
230- userSrv.getFromId(request, value.toLowerCase, " pki" )
231- }
232- } yield fieldValue
233- }
234- }
235- .getOrElse(Future .failed(AuthenticationError (" Certificate doesn't contain user information" )))
236- }
237-
238146 def getFromHeader (request : RequestHeader ): Future [AuthContext ] =
239147 for {
240148 header <- authHeaderName.fold[Future [String ]](Future .failed(AuthenticationError (" HTTP header is not configured" )))(Future .successful)
@@ -244,7 +152,6 @@ class Authenticated(
244152
245153 val authenticationMethods : Seq [(String , RequestHeader => Future [AuthContext ])] =
246154 (if (authBySessionCookie) Seq (" session" -> getFromSession _) else Nil ) ++
247- (if (authByPki) Seq (" pki" -> getFromClientCertificate _) else Nil ) ++
248155 (if (authByKey) Seq (" key" -> getFromApiKey _) else Nil ) ++
249156 (if (authByBasicAuth) Seq (" basic" -> getFromBasicAuth _) else Nil ) ++
250157 (if (authByInitialUser) Seq (" init" -> userSrv.getInitialUser _) else Nil ) ++
0 commit comments