All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gatling.http.auth.DigestWwwAuthenticateHeaderParser.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/*
 * Copyright 2011-2023 GatlingCorp (https://gatling.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.gatling.http.auth

import java.util.Locale

import scala.util.parsing.combinator.RegexParsers

import io.gatling.commons.validation._
import io.gatling.http.auth.DigestAuth.Algorithm
import io.gatling.http.client.uri.Uri

private[auth] object DigestWwwAuthenticateHeaderParser {
  private val AttributeNameRegex = "[a-z]+".r
  private val QuotedAttributeValueRegex = "[^\"]+".r
  private val UnquotedAttributeValueRegex = "[^\\s,]+".r
}

private[auth] final class DigestWwwAuthenticateHeaderParser extends RegexParsers {
  import DigestWwwAuthenticateHeaderParser._

  private val attributeName: Parser[String] = AttributeNameRegex
  private val quotedAttributeValue: Parser[String] = "\"" ~> QuotedAttributeValueRegex <~ "\""
  private val unquotedAttributeValue: Parser[String] = UnquotedAttributeValueRegex
  private val attributeValue: Parser[String] = quotedAttributeValue | unquotedAttributeValue
  private val attribute: Parser[(String, String)] = ((attributeName <~ "=") ~ attributeValue) ^^ { case name ~ value => name -> value }
  private val parser: Parser[Map[String, String]] = ("Digest " ~> repsep(attribute, ",")) ^^ {
    _.toMap
  }

  private def parseAlgorithm(algorithm: String): Option[DigestAuth.Algorithm] =
    algorithm match {
      case DigestAuth.Algorithm.Md5.value           => Some(DigestAuth.Algorithm.Md5)
      case DigestAuth.Algorithm.Md5Sess.value       => Some(DigestAuth.Algorithm.Md5Sess)
      case DigestAuth.Algorithm.Sha256.value        => Some(DigestAuth.Algorithm.Sha256)
      case DigestAuth.Algorithm.Sha256Sess.value    => Some(DigestAuth.Algorithm.Sha256Sess)
      case DigestAuth.Algorithm.Sha512256.value     => Some(DigestAuth.Algorithm.Sha512256)
      case DigestAuth.Algorithm.Sha512256Sess.value => Some(DigestAuth.Algorithm.Sha512256Sess)
      case _                                        => None
    }

  private def parseQop(qopOpt: Option[String]): Validation[DigestAuth.Qop] =
    qopOpt match {
      case Some(qop) =>
        val values = qop.split(",").map(_.trim)
        if (values.contains(DigestAuth.Qop.Auth.value)) {
          DigestAuth.Qop.Auth.success
        } else if (values.contains(DigestAuth.Qop.AuthInt.value)) {
          "Gatling doesn't support qop=auth-int".failure
        } else {
          DigestAuth.Qop.Auth.success
        }
      case _ => DigestAuth.Qop.Auth.success
    }

  private def parseDomain(domainOpt: Option[String], origin: Uri): Set[DigestAuth.ProtectionSpace] =
    domainOpt match {
      case Some(domain) =>
        domain
          .split(",")
          .map { url =>
            val uri = Uri.create(origin, url.trim)
            DigestAuth.ProtectionSpace(uri.getHost.toLowerCase(Locale.ROOT), uri.getLastDirectoryPath)
          }
          .toSet

      case _ => Set(DigestAuth.ProtectionSpace(origin.getHost.toLowerCase(Locale.ROOT), "/"))
    }

  def parse(header: String, origin: Uri): Validation[DigestAuth.Challenge] =
    safely() {
      parseAll(parser, header) match {
        case Success(attributes, _) =>
          for {
            realm <- attributes.get("realm").toValidation("realm attribute is missing")
            nonce <- attributes.get("nonce").toValidation("nonce attribute is missing")
            qop <- parseQop(attributes.get("qop"))
          } yield DigestAuth.Challenge(
            realm,
            parseDomain(attributes.get("domain"), origin),
            nonce,
            attributes.get("opaque"),
            attributes.get("stale").exists(_.toBoolean),
            attributes.get("algorithm").flatMap(parseAlgorithm).getOrElse(Algorithm.Md5),
            qop,
            attributes.get("userhash").exists(_.toBoolean)
          )
        case ns: NoSuccess => s"Failed to parse header $header: ${ns.msg.failure}".failure
      }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy