<commit_before>package com.ubirch.util.oidc.directive

import akka.actor.ActorSystem
import akka.http.scaladsl.HttpExt
import akka.http.scaladsl.model.headers.{Authorization, OAuth2BearerToken}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{AuthorizationFailedRejection, Directive1}
import akka.stream.Materializer
import com.typesafe.scalalogging.slf4j.StrictLogging
import com.ubirch.user.client.rest.UserServiceClientRest
import com.ubirch.util.config.ConfigBase
import com.ubirch.util.json.{Json4sUtil, JsonFormats}
import com.ubirch.util.oidc.config.OidcUtilsConfig
import com.ubirch.util.oidc.model.UserContext
import com.ubirch.util.oidc.util.OidcUtil
import com.ubirch.util.redis.RedisClientUtil
import org.joda.time.{DateTime, DateTimeZone}
import org.json4s.Formats
import org.json4s.native.Serialization.read
import redis.RedisClient

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.language.postfixOps

/**
  * author: cvandrei
  * since: 2017-03-17
  */
class OidcDirective()(implicit system: ActorSystem, httpClient: HttpExt, materializer: Materializer)
  extends StrictLogging
    with ConfigBase {

  implicit private val formatter: Formats = JsonFormats.default

  private val envid = config.getString("ubirch.envid").toLowerCase
  private val skipSignatureChecking = config.getBoolean("ubirch.oidcUtils.skipSignatureChecking")
  private val maxTokenAge = config.getInt("ubirch.oidcUtils.maxTokenAge")
  private val skipTokenAgeCheck = config.getInt("ubirch.oidcUtils.maxTokenAge")

  val bearerToken: Directive1[Option[String]] =
    optionalHeaderValueByType(classOf[Authorization]).map(extractBearerToken)

  val ubirchToken: Directive1[Option[String]] =
    optionalHeaderValueByName("Authorization")

  val oidcToken2UserContext: Directive1[UserContext] = {

    bearerToken flatMap {

      case None =>

        ubirchToken flatMap {

          case None =>
            reject(AuthorizationFailedRejection)

          case Some(ubToken: String) =>

            onComplete(ubTokenToUserContext(ubToken = ubToken)) flatMap { u =>

              u.map(provide).recover {

                case _: VerificationException =>
                  logger.error(s"Unable to log in with provided token: $ubToken")
                  reject(AuthorizationFailedRejection).toDirective[Tuple1[UserContext]]

              }.get

            }

        }

      case Some(token: String) =>

        onComplete(tokenToUserContext(token = token)) flatMap { u =>

          u.map(provide).recover {

            case _: VerificationException =>
              logger.error(s"Unable to log in with provided token: $token")
              reject(AuthorizationFailedRejection).toDirective[Tuple1[UserContext]]

          }.get

        }
    }

  }

  private def ubTokenToUserContext(ubToken: String)(implicit httpClient: HttpExt, materializer: Materializer): Future[UserContext] = {
    val redis = RedisClientUtil.getRedisClient
    redis.get[String](ubToken) flatMap {

      case None =>
        val splt = ubToken.split("::")
        if (splt.size == 3) {
          val context = splt(0).toLowerCase.replace("bearer", ""). trim

          if (!envid.equals(context)) {
            logger.debug(s"invalid enviroment id: $context")
            throw new VerificationException()
          }

          val token = splt(1)
          val tokenParts = token.split("##")
          val extUserId = tokenParts(0)
          val tsStr = if (tokenParts.size >= 2)
            Some(tokenParts(1))
          else
            None
          val signature = splt(2)

          UserServiceClientRest.userGET(providerId = "ubirchToken", externalUserId = extUserId).map {
            case Some(user) if user.id.isDefined && user.activeUser =>

              if (skipSignatureChecking || checkSignature(extUserId, token, signature, tsStr)) {

                val uc = UserContext(
                  context = context,<commit_msg>little improvements<commit_after>package com.ubirch.util.oidc.directive

import akka.actor.ActorSystem
import akka.http.scaladsl.HttpExt
import akka.http.scaladsl.model.headers.{Authorization, OAuth2BearerToken}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{AuthorizationFailedRejection, Directive1}
import akka.stream.Materializer
import com.typesafe.scalalogging.slf4j.StrictLogging
import com.ubirch.user.client.rest.UserServiceClientRest
import com.ubirch.util.config.ConfigBase
import com.ubirch.util.json.{Json4sUtil, JsonFormats}
import com.ubirch.util.oidc.config.OidcUtilsConfig
import com.ubirch.util.oidc.model.UserContext
import com.ubirch.util.oidc.util.OidcUtil
import com.ubirch.util.redis.RedisClientUtil
import org.joda.time.{DateTime, DateTimeZone}
import org.json4s.Formats
import org.json4s.native.Serialization.read
import redis.RedisClient

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.language.postfixOps

/**
  * author: cvandrei
  * since: 2017-03-17
  */
class OidcDirective()(implicit system: ActorSystem, httpClient: HttpExt, materializer: Materializer)
  extends StrictLogging
    with ConfigBase {

  implicit private val formatter: Formats = JsonFormats.default

  private val envid = config.getString("ubirch.envid").toLowerCase
  private val skipSignatureChecking = config.getBoolean("ubirch.oidcUtils.skipSignatureChecking")
  private val maxTokenAge = config.getInt("ubirch.oidcUtils.maxTokenAge")
  private val skipTokenAgeCheck = config.getInt("ubirch.oidcUtils.maxTokenAge")

  val bearerToken: Directive1[Option[String]] =
    optionalHeaderValueByType(classOf[Authorization]).map(extractBearerToken)

  val ubirchToken: Directive1[Option[String]] =
    optionalHeaderValueByName("Authorization")

  val oidcToken2UserContext: Directive1[UserContext] = {

    bearerToken flatMap {

      case None =>

        ubirchToken flatMap {

          case None =>
            reject(AuthorizationFailedRejection)

          case Some(ubToken: String) =>

            onComplete(ubTokenToUserContext(ubToken = ubToken)) flatMap { u =>

              u.map(provide).recover {

                case _: VerificationException =>
                  logger.error(s"Unable to log in with provided token: $ubToken")
                  reject(AuthorizationFailedRejection).toDirective[Tuple1[UserContext]]

              }.get

            }

        }

      case Some(token: String) =>

        onComplete(tokenToUserContext(token = token)) flatMap { u =>

          u.map(provide).recover {

            case _: VerificationException =>
              logger.error(s"Unable to log in with provided token: $token")
              reject(AuthorizationFailedRejection).toDirective[Tuple1[UserContext]]

          }.get

        }
    }

  }

  private def ubTokenToUserContext(ubToken: String)(implicit httpClient: HttpExt, materializer: Materializer): Future[UserContext] = {
    val redis = RedisClientUtil.getRedisClient
    redis.get[String](ubToken) flatMap {

      case None =>
        val splt = ubToken.split("::")
        if (splt.size == 3) {
          val context = splt(0).toLowerCase.replace("bearer", ""). trim

          if (!envid.equals(context)) {
            logger.debug(s"invalid enviroment id: $context")
            throw new VerificationException()
          }

          val token = splt(1)
          val tokenParts = token.split("##")
          val extUserId = tokenParts(0).toLowerCase.trim
          val tsStr = if (tokenParts.size >= 2)
            Some(tokenParts(1))
          else
            None
          val signature = splt(2)

          UserServiceClientRest.userGET(providerId = "ubirchToken", externalUserId = extUserId).map {
            case Some(user) if user.id.isDefined && user.activeUser =>

              if (skipSignatureChecking || checkSignature(extUserId, token, signature, tsStr)) {

                val uc = UserContext(
                  context = context,
{'fraction_non_alphanumeric': 0.09843345257185149, 'fraction_numerical': 0.006660910324411003, 'mean_word_length': 3.162217659137577, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '984980', 'n_tokens_mistral': 2456, 'n_tokens_neox': 2390, 'n_words': 448}