cats.data.OptionT Scala Examples

The following examples show how to use cats.data.OptionT. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: NatchezHttp4sModule.scala    From skunk   with MIT License 8 votes vote down vote up
// Copyright (c) 2018-2020 by Rob Norris
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT

package natchez.http4s

import cats.~>
import cats.data.{ Kleisli, OptionT }
import cats.effect.Bracket
import cats.implicits._
import natchez.{ EntryPoint, Kernel, Span }
import org.http4s.HttpRoutes
import natchez.Trace
import natchez.Tags
import scala.util.control.NonFatal
import org.http4s.Response
import cats.effect.Resource
import cats.Defer
import natchez.TraceValue
import cats.Monad

object implicits {

  // Given an entry point and HTTP Routes in Kleisli[F, Span[F], ?] return routes in F. A new span
  // is created with the URI path as the name, either as a continuation of the incoming trace, if
  // any, or as a new root. This can likely be simplified, I just did what the types were saying
  // and it works so :shrug:
  private def liftT[F[_]: Bracket[?[_], Throwable]](
    entryPoint: EntryPoint[F])(
    routes:     HttpRoutes[Kleisli[F, Span[F], ?]]
  ): HttpRoutes[F] =
    Kleisli { req =>
      type G[A]  = Kleisli[F, Span[F], A]
      val lift   = λ[F ~> G](fa => Kleisli(_ => fa))
      val kernel = Kernel(req.headers.toList.map(h => (h.name.value -> h.value)).toMap)
      val spanR  = entryPoint.continueOrElseRoot(req.uri.path, kernel)
      OptionT {
        spanR.use { span =>
          val lower = λ[G ~> F](_(span))
          routes.run(req.mapK(lift)).mapK(lower).map(_.mapK(lower)).value
        }
      }
    }

  implicit class EntryPointOps[F[_]](self: EntryPoint[F]) {

    private def dummySpan(
      implicit ev: Monad[F]
    ): Span[F] =
      new Span[F] {
        val kernel: F[Kernel] = Kernel(Map.empty).pure[F]
        def put(fields: (String, TraceValue)*): F[Unit] = Monad[F].unit
        def span(name: String): Resource[F, Span[F]] = Monad[Resource[F, ?]].pure(this)
      }

    def liftT(routes: HttpRoutes[Kleisli[F, Span[F], ?]])(
      implicit ev: Bracket[F, Throwable]
    ): HttpRoutes[F] =
      implicits.liftT(self)(routes)

    
  def natchezMiddleware[F[_]: Bracket[?[_], Throwable]: Trace](routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli { req =>

      val addRequestFields: F[Unit] =
        Trace[F].put(
          Tags.http.method(req.method.name),
          Tags.http.url(req.uri.renderString)
        )

      def addResponseFields(res: Response[F]): F[Unit] =
        Trace[F].put(
          Tags.http.status_code(res.status.code.toString)
        )

      def addErrorFields(e: Throwable): F[Unit] =
        Trace[F].put(
          Tags.error(true),
          "error.message"    -> e.getMessage,
          "error.stacktrace" -> e.getStackTrace.mkString("\n"),
        )

      OptionT {
        routes(req).onError {
          case NonFatal(e)   => OptionT.liftF(addRequestFields *> addErrorFields(e))
        } .value.flatMap {
          case Some(handler) => addRequestFields *> addResponseFields(handler).as(handler.some)
          case None          => Option.empty[Response[F]].pure[F]
        }
      }
    }

} 
Example 2
Source File: ref.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.concurrent.syntax
import cats.data.OptionT
import cats.effect.Resource
import cats.effect.concurrent.Ref
import cats.{Functor, Monad}
import tofu.BracketThrow
import tofu.concurrent.impl.FocusedRef
import tofu.optics.{Contains, PProperty}
import tofu.syntax.monadic._

object ref {
  implicit final class TofuRefOps[F[_], A](private val self: Ref[F, A]) extends AnyVal {
    def focused[B](focus: A Contains B)(implicit F: Functor[F]): Ref[F, B] = FocusedRef(self, focus)

    
    def optimisticModifyRes[B, X, R](prop: PProperty[A, A, R, X])(init: => Resource[F, X])(f: X => R)(implicit
        F: BracketThrow[F]
    ): F[R] =
      OptionT(self.get.map(prop.downcast)).getOrElseF(
        init.use(x => self.modify(a => prop.downcast(a).fold((prop.set(a, x), f(x)))((a, _))))
      )
  }
} 
Example 3
Source File: CfpCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.api.published

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Cfp, CommonCfp, ExternalCfp}
import gospeak.core.services.storage.{PublicExternalCfpRepo, PublicGroupRepo}
import gospeak.web.AppConf
import gospeak.web.api.domain.ApiCfp
import gospeak.web.api.domain.utils.ApiResult
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.utils.ApiCtrl
import gospeak.libs.scala.domain.Page
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class CfpCtrl(cc: ControllerComponents,
              silhouette: Silhouette[CookieEnv],
              conf: AppConf,
              groupRepo: PublicGroupRepo,
              externalCfpRepo: PublicExternalCfpRepo) extends ApiCtrl(cc, silhouette, conf) {
  def list(params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiCfp.Published]] { implicit req =>
    for {
      cfps <- externalCfpRepo.listIncoming(params)
      groups <- groupRepo.list(cfps.items.flatMap(_.internal.map(_.group.id)))
    } yield ApiResult.of(cfps, (cfp: CommonCfp) => ApiCfp.published(cfp, groups))
  }

  def detail(cfp: Cfp.Slug): Action[AnyContent] = UserAwareAction[ApiCfp.Published] { implicit req =>
    (for {
      cfpElt <- OptionT(externalCfpRepo.findCommon(cfp))
      groups <- OptionT.liftF(groupRepo.list(cfpElt.internal.map(_.group.id).toList))
      res = ApiResult.of(ApiCfp.published(cfpElt, groups))
    } yield res).value.map(_.getOrElse(cfpNotFound(cfp)))
  }

  def detailExt(cfp: ExternalCfp.Id): Action[AnyContent] = UserAwareAction[ApiCfp.Published] { implicit req =>
    (for {
      cfpElt <- OptionT(externalCfpRepo.findCommon(cfp))
      groups <- OptionT.liftF(groupRepo.list(cfpElt.internal.map(_.group.id).toList))
      res = ApiResult.of(ApiCfp.published(cfpElt, groups))
    } yield res).value.map(_.getOrElse(cfpNotFound(cfp)))
  }
} 
Example 4
Source File: SpeakerCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.pages.orga.speakers

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Group, User}
import gospeak.core.services.storage.{OrgaEventRepo, OrgaGroupRepo, OrgaProposalRepo, OrgaUserRepo}
import gospeak.web.AppConf
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.domain.Breadcrumb
import gospeak.web.pages.orga.GroupCtrl
import gospeak.web.pages.orga.speakers.SpeakerCtrl._
import gospeak.web.utils.{OrgaReq, UICtrl}
import gospeak.libs.scala.domain.Page
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class SpeakerCtrl(cc: ControllerComponents,
                  silhouette: Silhouette[CookieEnv],
                  conf: AppConf,
                  userRepo: OrgaUserRepo,
                  val groupRepo: OrgaGroupRepo,
                  eventRepo: OrgaEventRepo,
                  proposalRepo: OrgaProposalRepo) extends UICtrl(cc, silhouette, conf) with UICtrl.OrgaAction {
  def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req =>
    userRepo.speakers(params).map(speakers => Ok(html.list(speakers)(listBreadcrumb)))
  }

  def detail(group: Group.Slug, speaker: User.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req =>
    (for {
      speakerElt <- OptionT(userRepo.find(speaker))
      proposals <- OptionT.liftF(proposalRepo.listFull(speakerElt.id, params))
      speakers <- OptionT.liftF(userRepo.list(proposals.items.flatMap(_.users)))
      userRatings <- OptionT.liftF(proposalRepo.listRatings(proposals.items.map(_.id)))
      res = Ok(html.detail(speakerElt, proposals, speakers, userRatings)(breadcrumb(speakerElt)))
    } yield res).value.map(_.getOrElse(speakerNotFound(group, speaker)))
  }
}

object SpeakerCtrl {
  def listBreadcrumb(implicit req: OrgaReq[AnyContent]): Breadcrumb =
    GroupCtrl.breadcrumb.add("Speakers" -> routes.SpeakerCtrl.list(req.group.slug))

  def breadcrumb(speaker: User)(implicit req: OrgaReq[AnyContent]): Breadcrumb =
    listBreadcrumb.add(speaker.name.value -> routes.SpeakerCtrl.detail(req.group.slug, speaker.slug))
} 
Example 5
Source File: CfpCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.pages.orga.cfps

import cats.data.OptionT
import cats.effect.IO
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.utils.OrgaCtx
import gospeak.core.domain.{Cfp, Event, Group}
import gospeak.core.services.storage._
import gospeak.web.AppConf
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.domain.Breadcrumb
import gospeak.web.pages.orga.GroupCtrl
import gospeak.web.pages.orga.cfps.CfpCtrl._
import gospeak.web.pages.orga.events.routes.{EventCtrl => EventRoutes}
import gospeak.web.utils.{GsForms, OrgaReq, UICtrl}
import gospeak.libs.scala.domain.Page
import play.api.data.Form
import play.api.mvc.{Action, AnyContent, ControllerComponents, Result}

class CfpCtrl(cc: ControllerComponents,
              silhouette: Silhouette[CookieEnv],
              conf: AppConf,
              userRepo: OrgaUserRepo,
              val groupRepo: OrgaGroupRepo,
              cfpRepo: OrgaCfpRepo,
              eventRepo: OrgaEventRepo,
              proposalRepo: OrgaProposalRepo) extends UICtrl(cc, silhouette, conf) with UICtrl.OrgaAction {
  def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req =>
    val customParams = params.withNullsFirst
    cfpRepo.list(customParams).map(cfps => Ok(html.list(cfps)(listBreadcrumb))) // TODO listWithProposalCount
  }

  def create(group: Group.Slug, event: Option[Event.Slug]): Action[AnyContent] = OrgaAction(group) { implicit req =>
    createView(group, GsForms.cfp, event)
  }

  def doCreate(group: Group.Slug, event: Option[Event.Slug]): Action[AnyContent] = OrgaAction(group) { implicit req =>
    GsForms.cfp.bindFromRequest.fold(
      formWithErrors => createView(group, formWithErrors, event),
      data => (for {
        // TODO check if slug not already exist
        cfpElt <- OptionT.liftF(cfpRepo.create(data))
        redirect <- OptionT.liftF(event.map { e =>
          eventRepo.attachCfp(e, cfpElt.id)
            .map(_ => Redirect(EventRoutes.detail(group, e)))
          // TODO recover and redirect to cfp detail
        }.getOrElse {
          IO.pure(Redirect(routes.CfpCtrl.detail(group, data.slug)))
        })
      } yield redirect).value.map(_.getOrElse(groupNotFound(group)))
    )
  }

  private def createView(group: Group.Slug, form: Form[Cfp.Data], event: Option[Event.Slug])(implicit req: OrgaReq[AnyContent]): IO[Result] = {
    val b = listBreadcrumb.add("New" -> routes.CfpCtrl.create(group))
    IO.pure(Ok(html.create(form, event)(b)))
  }

  def detail(group: Group.Slug, cfp: Cfp.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req =>
    (for {
      cfpElt <- OptionT(cfpRepo.find(cfp))
      proposals <- OptionT.liftF(proposalRepo.listFull(cfp, params))
      speakers <- OptionT.liftF(userRepo.list(proposals.items.flatMap(_.users).distinct))
      userRatings <- OptionT.liftF(proposalRepo.listRatings(cfp))
      b = breadcrumb(cfpElt)
    } yield Ok(html.detail(cfpElt, proposals, speakers, userRatings)(b))).value.map(_.getOrElse(cfpNotFound(group, cfp)))
  }

  def edit(group: Group.Slug, cfp: Cfp.Slug, redirect: Option[String]): Action[AnyContent] = OrgaAction(group) { implicit req =>
    editView(group, cfp, GsForms.cfp, redirect)
  }

  def doEdit(group: Group.Slug, cfp: Cfp.Slug, redirect: Option[String]): Action[AnyContent] = OrgaAction(group) { implicit req =>
    GsForms.cfp.bindFromRequest.fold(
      formWithErrors => editView(group, cfp, formWithErrors, redirect),
      data => (for {
        cfpOpt <- OptionT.liftF(cfpRepo.find(data.slug))
        res <- OptionT.liftF(cfpOpt match {
          case Some(duplicate) if data.slug != cfp => editView(group, cfp, GsForms.cfp.fillAndValidate(data).withError("slug", s"Slug already taken by cfp: ${duplicate.name.value}"), redirect)
          case _ => cfpRepo.edit(cfp, data).map { _ => redirectOr(redirect, routes.CfpCtrl.detail(group, data.slug)) }
        })
      } yield res).value.map(_.getOrElse(groupNotFound(group)))
    )
  }

  private def editView(group: Group.Slug, cfp: Cfp.Slug, form: Form[Cfp.Data], redirect: Option[String])(implicit req: OrgaReq[AnyContent], ctx: OrgaCtx): IO[Result] = {
    (for {
      cfpElt <- OptionT(cfpRepo.find(cfp))
      filledForm = if (form.hasErrors) form else form.fill(cfpElt.data)
      b = breadcrumb(cfpElt).add("Edit" -> routes.CfpCtrl.edit(group, cfp))
    } yield Ok(html.edit(cfpElt, filledForm, redirect)(b))).value.map(_.getOrElse(cfpNotFound(group, cfp)))
  }
}

object CfpCtrl {
  def listBreadcrumb(implicit req: OrgaReq[AnyContent]): Breadcrumb =
    GroupCtrl.breadcrumb.add("CFPs" -> routes.CfpCtrl.list(req.group.slug))

  def breadcrumb(cfp: Cfp)(implicit req: OrgaReq[AnyContent]): Breadcrumb =
    listBreadcrumb.add(cfp.name.value -> routes.CfpCtrl.detail(req.group.slug, cfp.slug))
} 
Example 6
Source File: VideoCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.pages.published.videos

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.Video
import gospeak.core.services.storage.PublicVideoRepo
import gospeak.infra.services.EmbedSrv
import gospeak.libs.scala.domain.{Html, Page, Url}
import gospeak.web.AppConf
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.domain.Breadcrumb
import gospeak.web.pages.published.HomeCtrl
import gospeak.web.pages.published.videos.VideoCtrl._
import gospeak.web.utils.UICtrl
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class VideoCtrl(cc: ControllerComponents,
                silhouette: Silhouette[CookieEnv],
                conf: AppConf,
                videoRepo: PublicVideoRepo) extends UICtrl(cc, silhouette, conf) {
  def list(params: Page.Params): Action[AnyContent] = UserAwareAction { implicit req =>
    videoRepo.list(params).map(videos => Ok(html.list(videos)(listBreadcrumb())))
  }

  def detail(video: Url.Video.Id): Action[AnyContent] = UserAwareAction { implicit req =>
    (for {
      videoElt <- OptionT(videoRepo.find(video))
      embed: Html <- OptionT.liftF(EmbedSrv.embedCode(videoElt.url))
      b = breadcrumb(videoElt)
    } yield Ok(html.detail(videoElt, embed)(b))).value.map(_.getOrElse(publicVideoNotFound(video)))
  }
}

object VideoCtrl {
  def listBreadcrumb(): Breadcrumb =
    HomeCtrl.breadcrumb().add("Videos" -> routes.VideoCtrl.list())

  def breadcrumb(video: Video): Breadcrumb =
    listBreadcrumb().add(video.title -> routes.VideoCtrl.detail(video.id))
} 
Example 7
Source File: SpeakerCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.pages.published.speakers

import cats.data.OptionT
import cats.effect.IO
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Proposal, Talk, User}
import gospeak.core.services.email.EmailSrv
import gospeak.core.services.storage._
import gospeak.libs.scala.domain.Page
import gospeak.web.AppConf
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.domain.Breadcrumb
import gospeak.web.emails.Emails
import gospeak.web.pages.published.HomeCtrl
import gospeak.web.pages.published.speakers.SpeakerCtrl._
import gospeak.web.utils._
import play.api.mvc._

class SpeakerCtrl(cc: ControllerComponents,
                  silhouette: Silhouette[CookieEnv],
                  conf: AppConf,
                  userRepo: PublicUserRepo,
                  talkRepo: PublicTalkRepo,
                  externalProposalRepo: PublicExternalProposalRepo,
                  groupRepo: PublicGroupRepo,
                  emailSrv: EmailSrv) extends UICtrl(cc, silhouette, conf) {
  def list(params: Page.Params): Action[AnyContent] = UserAwareAction { implicit req =>
    userRepo.listPublic(params).map(speakers => Ok(html.list(speakers)(listBreadcrumb())))
  }

  def detail(user: User.Slug): Action[AnyContent] = UserAwareAction { implicit req =>
    (for {
      speakerElt <- OptionT(userRepo.findPublic(user))
      groups <- OptionT.liftF(groupRepo.listFull(speakerElt.id))
      talks <- OptionT.liftF(talkRepo.listAll(speakerElt.id, Talk.Status.Public))
      proposals <- OptionT.liftF(externalProposalRepo.listAllCommon(speakerElt.id, Proposal.Status.Accepted))
      users <- OptionT.liftF(userRepo.list((groups.flatMap(_.owners.toList) ++ talks.flatMap(_.users)).distinct))
      res = Ok(html.detail(speakerElt, groups, talks, proposals.groupBy(_.talk.id), users)(breadcrumb(speakerElt)))
    } yield res).value.map(_.getOrElse(publicUserNotFound(user)))
  }

  def talk(user: User.Slug, talk: Talk.Slug): Action[AnyContent] = UserAwareAction { implicit req =>
    (for {
      userElt <- OptionT(userRepo.findPublic(user))
      talkElt <- OptionT(talkRepo.findPublic(talk, userElt.id))
      proposals <- OptionT.liftF(externalProposalRepo.listAllCommon(talkElt.id, Proposal.Status.Accepted))
      users <- OptionT.liftF(userRepo.list((talkElt.users ++ proposals.flatMap(_.users)).distinct))
      res = Ok(html.talk(userElt, talkElt, proposals, users)(breadcrumb(userElt, talkElt)))
    } yield res).value.map(_.getOrElse(publicTalkNotFound(user, talk)))
  }

  def contactSpeaker(user: User.Slug): Action[AnyContent] = UserAction { implicit req =>
    val next = Redirect(routes.SpeakerCtrl.detail(user))
    GsForms.speakerContact.bindFromRequest.fold(
      formWithErrors => IO.pure(next.flashing(formWithErrors.flash)),
      data => (for {
        speakerElt <- OptionT(userRepo.findPublic(user)(req.userAware))
        _ <- OptionT.liftF(emailSrv.send(Emails.contactSpeaker(data.subject, data.content, speakerElt.user)))
        res = next.flashing("success" -> "The message has been sent!")
      } yield res).value.map(_.getOrElse(publicUserNotFound(user))))
  }
}

object SpeakerCtrl {
  def listBreadcrumb(): Breadcrumb =
    HomeCtrl.breadcrumb().add("Speakers" -> routes.SpeakerCtrl.list())

  def breadcrumb(speaker: User.Full): Breadcrumb =
    listBreadcrumb().add(speaker.name.value -> routes.SpeakerCtrl.detail(speaker.slug))

  def breadcrumb(speaker: User.Full, talk: Talk): Breadcrumb =
    breadcrumb(speaker).add("Talks" -> routes.SpeakerCtrl.detail(speaker.slug)).add(talk.title.value -> routes.SpeakerCtrl.talk(speaker.slug, talk.slug))
} 
Example 8
Source File: CfpCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.pages.user.talks.cfps

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.Talk
import gospeak.core.services.storage.{SpeakerCfpRepo, SpeakerProposalRepo, SpeakerTalkRepo}
import gospeak.web.AppConf
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.domain.Breadcrumb
import gospeak.web.pages.user.talks.TalkCtrl
import gospeak.web.pages.user.talks.cfps.CfpCtrl._
import gospeak.web.utils.{UICtrl, UserReq}
import gospeak.libs.scala.domain.Page
import play.api.mvc._

class CfpCtrl(cc: ControllerComponents,
              silhouette: Silhouette[CookieEnv],
              conf: AppConf,
              cfpRepo: SpeakerCfpRepo,
              talkRepo: SpeakerTalkRepo,
              proposalRepo: SpeakerProposalRepo) extends UICtrl(cc, silhouette, conf) {
  def list(talk: Talk.Slug, params: Page.Params): Action[AnyContent] = UserAction { implicit req =>
    (for {
      talkElt <- OptionT(talkRepo.find(talk))
      cfps <- OptionT.liftF(cfpRepo.availableFor(talkElt.id, params))
      b = listBreadcrumb(talkElt)
    } yield Ok(html.list(talkElt, cfps)(b))).value.map(_.getOrElse(talkNotFound(talk)))
  }
}

object CfpCtrl {
  def listBreadcrumb(talk: Talk)(implicit req: UserReq[AnyContent]): Breadcrumb =
    TalkCtrl.breadcrumb(talk).add("Proposing" -> routes.CfpCtrl.list(talk.slug))
} 
Example 9
Source File: Selective.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.control

import cats.data.{EitherT, OptionT, ReaderT, WriterT}
import cats.instances.option._
import cats.syntax.coflatMap._
import cats.syntax.option._
import cats.{Applicative, Monad, Monoid}
import simulacrum._
import tofu.control.impl._

@typeclass
trait Selective[F[_]] extends Applicative[F] {
  @noop def selectAp[A, B](fe: F[Either[A, B]])(ff: => F[A => B]): F[B]

  @noop def select[A](fo: F[Option[A]])(fa: => F[A]): F[A] =
    selectAp[Unit, A](map(fo)(_.toRight(())))(map(fa)(a => (_: Unit) => a))

  def selectRight[A](fb: F[A], fo: F[Option[A]]): F[A] = select(fo)(fb)

  @noop def orElses[A](fx: F[Option[A]])(fy: => F[Option[A]]): F[Option[A]] = select(map(fx)(_.coflatten))(fy)

  def whens[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] =
    select(map(fb)(x => if (x) None else Some(none[A])))(map(fa)(_.some))

  def unlesss[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] =
    select(map(fb)(x => if (x) Some(none[A]) else None))(map(fa)(_.some))

  def whens_[A](fb: F[Boolean])(fa: => F[A]): F[Unit] =
    select(map(fb)(x => if (x) None else Some(())))(void(fa))

  def unlesss_[A](fb: F[Boolean])(fa: => F[A]): F[Unit] =
    select(map(fb)(x => if (x) Some(()) else None))(void(fa))

  @noop def optionMonoid[A]: Monoid[F[Option[A]]] = new Monoid[F[Option[A]]] {
    def empty: F[Option[A]]                                     = pure(None)
    def combine(x: F[Option[A]], y: F[Option[A]]): F[Option[A]] = orElses(x)(y)
  }
}

object Selective extends SelectiveInstances

trait SelectiveInstances extends SelectiveInstances2 {
  final implicit def selectiveOverMonad[F[_]: Monad]: SelectiveOverMonad[F] = new SelectiveOverMonad[F]
}
trait SelectiveInstances2 {
  final implicit def selectiveOptionT[F[_]: Selective]: Selective[OptionT[F, *]]               = new SelectiveOptionT[F]
  final implicit def selectiveEitherT[F[_]: Selective, E]: Selective[EitherT[F, E, *]]         = new SelectiveEitherT[F, E]
  final implicit def selectiveReaderT[F[_]: Selective, R]: Selective[ReaderT[F, R, *]]         = new SelectiveReaderT[F, R]
  final implicit def selectiveWriterT[F[_]: Selective, W: Monoid]: Selective[WriterT[F, W, *]] =
    new SelectiveWriterT[F, W]
} 
Example 10
Source File: SelectiveInstances.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.control.impl
import cats.data.{EitherT, OptionT, ReaderT, WriterT}
import cats.instances.either._
import cats.instances.option._
import cats.instances.tuple._
import cats.{Applicative, Monad, Monoid}
import tofu.control.Selective
import tofu.syntax.either._
import tofu.syntax.monadic._

class SelectiveOverMonad[F[_]](implicit val F: Monad[F]) extends Selective[F] with ApplicativeDelegate[F] {
  def selectAp[A, B](fe: F[Either[A, B]])(ff: => F[A => B]): F[B] = F.flatMap(fe) {
    case Left(a)  => F.map(ff)(_(a))
    case Right(b) => F.pure(b)
  }

  override def select[A](fo: F[Option[A]])(fa: => F[A]): F[A] = F.flatMap(fo) {
    case None    => fa
    case Some(a) => F.pure(a)
  }

  override def whens[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]]   = F.flatMap(fb) {
    if (_) F.map(fa)(Some(_)) else F.pure(None)
  }
  override def unlesss[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] = F.flatMap(fb) {
    if (_) F.pure(None) else F.map(fa)(Some(_))
  }
  override def whens_[A](fb: F[Boolean])(fa: => F[A]): F[Unit]       = F.flatMap(fb) {
    if (_) F.unit else F.void(fa)
  }
  override def unlesss_[A](fb: F[Boolean])(fa: => F[A]): F[Unit]     = F.flatMap(fb) {
    if (_) F.void(fa) else F.unit
  }
}


trait SelectiveWithMap[F[_]] extends Selective[F] {
  def smap[A, B](fa: F[A])(f: A => B): F[B]

  def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] =
    selectAp[A, B](smap(fa)(Left(_)))(ff)

  override def map[A, B](fa: F[A])(f: A => B): F[B] = smap(fa)(f)
}

class SelectiveOptionT[F[_]](implicit F: Selective[F]) extends Selective[OptionT[F, *]] {
  def selectAp[A, B](fe: OptionT[F, Either[A, B]])(ff: => OptionT[F, A => B]): OptionT[F, B] =
    OptionT(F.selectAp(fe.value.map {
      case Some(Left(a))  => Left(a)
      case Some(Right(b)) => Right(Some(b))
      case None           => Right(None)
    })(ff.value.map {
      case Some(f) => b => Some(f(b))
      case None    => _ => None
    }))

  def ap[A, B](ff: OptionT[F, A => B])(fa: OptionT[F, A]): OptionT[F, B] =
    OptionT(F.ap[Option[A], Option[B]](ff.value.map(fo => (fo ap _)))(fa.value))

  def pure[A](x: A): OptionT[F, A] = OptionT.pure(x)
}

class SelectiveEitherT[F[_], L](implicit F: Selective[F]) extends Selective[EitherT[F, L, *]] {
  def selectAp[A, B](fe: EitherT[F, L, Either[A, B]])(ff: => EitherT[F, L, A => B]): EitherT[F, L, B] =
    EitherT(F.selectAp[A, Either[L, B]](fe.value.map {
      case Right(Left(a))       => Left(a)
      case rr @ Right(Right(_)) => rr.asInstanceOf[Either[A, Either[L, B]]]
      case l @ Left(_)          => Right(l.coerceR[B])
    })(ff.value.map {
      case Right(f)    => b => Right(f(b))
      case l @ Left(_) => _ => l.coerceR[B]
    }))

  def ap[A, B](ff: EitherT[F, L, A => B])(fa: EitherT[F, L, A]): EitherT[F, L, B] =
    EitherT(F.ap[Either[L, A], Either[L, B]](ff.value.map(fo => (fo ap _)))(fa.value))

  def pure[A](x: A): EitherT[F, L, A] = EitherT.pure(x)
}

class SelectiveReaderT[F[_], R](implicit FS: Selective[F])
    extends Selective[ReaderT[F, R, *]] with ApplicativeDelegate[ReaderT[F, R, *]] {
  val F: Applicative[ReaderT[F, R, *]] = implicitly

  def selectAp[A, B](fe: ReaderT[F, R, Either[A, B]])(ff: => ReaderT[F, R, A => B]): ReaderT[F, R, B] =
    ReaderT(r => FS.selectAp(fe.run(r))(ff.run(r)))
}

class SelectiveWriterT[F[_], W: Monoid](implicit FS: Selective[F])
    extends Selective[WriterT[F, W, *]] with ApplicativeDelegate[WriterT[F, W, *]] {
  val F: Applicative[WriterT[F, W, *]] = implicitly

  def selectAp[A, B](fe: WriterT[F, W, Either[A, B]])(ff: => WriterT[F, W, A => B]): WriterT[F, W, B] =
    WriterT(FS.selectAp[(W, A), (W, B)](FS.map(fe.run) {
      case (w, Left(a))  => Left((w, a))
      case (w, Right(b)) => Right((w, b))
    })(FS.map(ff.run)(wf => (wf ap _))))
} 
Example 11
Source File: GroupCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.api.published

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Event, Group, Proposal}
import gospeak.core.services.meetup.MeetupSrv
import gospeak.core.services.storage._
import gospeak.web.AppConf
import gospeak.web.api.domain._
import gospeak.web.api.domain.utils.ApiResult
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.utils.ApiCtrl
import gospeak.libs.scala.Extensions._
import gospeak.libs.scala.domain.Page
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class GroupCtrl(cc: ControllerComponents,
                silhouette: Silhouette[CookieEnv],
                conf: AppConf,
                groupRepo: PublicGroupRepo,
                eventRepo: PublicEventRepo,
                proposalRepo: PublicProposalRepo,
                venueRepo: PublicVenueRepo,
                userRepo: PublicUserRepo,
                groupSettingsRepo: PublicGroupSettingsRepo,
                meetupSrv: MeetupSrv) extends ApiCtrl(cc, silhouette, conf) {
  def list(params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiGroup.Published]] { implicit req =>
    groupRepo.listFull(params).map(ApiResult.of(_, ApiGroup.published))
  }

  def detail(group: Group.Slug): Action[AnyContent] = UserAwareAction[ApiGroup.Published] { implicit req =>
    groupRepo.findFull(group).map(_.map(g => ApiResult.of(ApiGroup.published(g))).getOrElse(groupNotFound(group)))
  }

  def events(group: Group.Slug, params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiEvent.Published]] { implicit req =>
    (for {
      groupElt <- OptionT(groupRepo.find(group))
      events <- OptionT.liftF(eventRepo.listPublished(groupElt.id, params))
      talks <- OptionT.liftF(proposalRepo.listPublic(events.items.flatMap(_.talks).distinct))
      speakers <- OptionT.liftF(userRepo.list(talks.flatMap(_.speakers.toList).distinct))
      res = ApiResult.of(events, ApiEvent.published(_, talks, speakers))
    } yield res).value.map(_.getOrElse(groupNotFound(group)))
  }

  def event(group: Group.Slug, event: Event.Slug): Action[AnyContent] = UserAwareAction[ApiEvent.Published] { implicit req =>
    (for {
      groupElt <- OptionT(groupRepo.find(group))
      eventElt <- OptionT(eventRepo.findPublished(groupElt.id, event))
      talks <- OptionT.liftF(proposalRepo.listPublic(eventElt.talks.distinct))
      speakers <- OptionT.liftF(userRepo.list(talks.flatMap(_.speakers.toList).distinct))
      res = ApiResult.of(ApiEvent.published(eventElt, talks, speakers))
    } yield res).value.map(_.getOrElse(eventNotFound(group, event)))
  }

  def eventDrawMeetupAttendee(group: Group.Slug, event: Event.Slug): Action[AnyContent] = UserAwareAction[Seq[ApiAttendee.Published]] { implicit req =>
    (for {
      groupElt <- OptionT(groupRepo.find(group))
      eventElt <- OptionT(eventRepo.findPublished(groupElt.id, event))
      creds <- OptionT(groupSettingsRepo.findMeetup(groupElt.id))
      attendees <- OptionT(eventElt.event.refs.meetup.map(r => meetupSrv.getAttendees(r.group, r.event, conf.app.aesKey, creds)).sequence)
      host = req.getQueryString("host")
      response = req.getQueryString("response")
      cleanAttendees = attendees.filter(a => a.id.value != 0L && response.forall(_ == a.response) && host.forall(_ == a.host.toString))
      res = ApiResult.of(cleanAttendees.map(ApiAttendee.published(_, creds.group)))
    } yield res).value.map(_.getOrElse(eventNotFound(group, event)))
  }

  def talks(group: Group.Slug, params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiProposal.Published]] { implicit req =>
    (for {
      groupElt <- OptionT(groupRepo.find(group))
      talks <- OptionT.liftF(proposalRepo.listPublicFull(groupElt.id, params))
      speakers <- OptionT.liftF(userRepo.list(talks.items.flatMap(_.speakers.toList).distinct))
      res = ApiResult.of(talks, ApiProposal.published(_, speakers))
    } yield res).value.map(_.getOrElse(groupNotFound(group)))
  }

  def talk(group: Group.Slug, talk: Proposal.Id): Action[AnyContent] = UserAwareAction[ApiProposal.Published] { implicit req =>
    (for {
      groupElt <- OptionT(groupRepo.find(group))
      talkElt <- OptionT(proposalRepo.findPublicFull(groupElt.id, talk))
      speakers <- OptionT.liftF(userRepo.list(talkElt.speakers.toList.distinct))
      res = ApiResult.of(ApiProposal.published(talkElt, speakers))
    } yield res).value.map(_.getOrElse(talkNotFound(group, talk)))
  }

  def speakers(group: Group.Slug, params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiUser.Published]] { implicit req =>
    (for {
      groupElt <- OptionT(groupRepo.find(group))
      speakers <- OptionT.liftF(userRepo.speakersPublic(groupElt.id, params))
      // TODO add proposals, talks, groups member and owners for each speaker
      res = ApiResult.of(speakers, ApiUser.published)
    } yield res).value.map(_.getOrElse(groupNotFound(group)))
  }
} 
Example 12
Source File: EmbedSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.higherKind.derived
import cats.data.{IorT, NonEmptyChain}
import cats.free.Free
import derevo.derive
import tofu.higherKind.Embed
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import cats.data.OptionT
import EmbedSuite.Foo
import tofu.syntax.monadic._
import tofu.syntax.embed._
import cats.instances.either._
import cats.instances.option._
import cats.syntax.traverse._
import cats.syntax.either._
import scala.util.Try

class EmbedSuite extends AnyFlatSpec with Matchers {
  val checkingFoo1: Foo[Either[String, *]] = new Foo[Either[String, *]] {
    override def foo(x: Int, s: String): Either[String, Double] =
      Try(s.toDouble).toEither.left.map(_ => s"could not parse $s as double").map(_ * x)
    override def bar(a: List[Int]): Either[String, Unit]        =
      a.headOption.toRight("must contain at least one element").void
    def baz(a: List[String]): OptionT[Either[String, *], Unit]  =
      OptionT(a.headOption.traverse(_.asLeft[Unit]))
  }

  "embed" should "generate nice embed" in {
    val rightFoo = checkingFoo1.asRight[String].embed
    val leftFoo  = "failed".asLeft[Foo[Either[String, *]]].embed

    rightFoo.foo(2, "2.3") should ===(Right(4.6))
    rightFoo.foo(2, "fail") should ===(Left("could not parse fail as double"))

    rightFoo.bar(List(4, 5, 6)) should ===(Right(()))
    rightFoo.bar(List()) should ===(Left("must contain at least one element"))

    leftFoo.foo(2, "2.3") should ===(Left("failed"))
    leftFoo.foo(2, "fail") should ===(Left("failed"))

    leftFoo.bar(List(4, 5, 6)) should ===(Left("failed"))
    leftFoo.bar(List()) should ===(Left("failed"))
  }
}

object EmbedSuite {
  @derive(embed)
  trait Foo[F[_]] {
    def foo(x: Int, s: String): F[Double]
    def bar(a: List[Int]): F[Unit]
    def baz(a: List[String]): OptionT[F, Unit]
  }

  Embed[Foo]

} 
Example 13
Source File: ArchiveCache.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.archives

import akka.actor.{ActorSystem, NotInfluenceReceiveTimeout}
import cats.Monad
import cats.data.OptionT
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.kg.archives.ArchiveCache._
import ch.epfl.bluebrain.nexus.kg.config.KgConfig.ArchivesConfig
import ch.epfl.bluebrain.nexus.kg.resources.ResId
import ch.epfl.bluebrain.nexus.sourcing.StateMachine
import ch.epfl.bluebrain.nexus.sourcing.akka.StopStrategy
import ch.epfl.bluebrain.nexus.sourcing.akka.statemachine.AkkaStateMachine
import retry.RetryPolicy

class ArchiveCache[F[_]: Monad](ref: StateMachine[F, String, State, Command, Unit]) {

  
  def put(value: Archive): OptionT[F, Archive] =
    OptionT(ref.evaluate(value.resId.show, Write(value)).map(_.toOption.flatten))

}

object ArchiveCache {

  private[archives] type State   = Option[Archive]
  private[archives] type Command = Write
  final private[archives] case class Write(bundle: Archive) extends NotInfluenceReceiveTimeout

  final def apply[F[_]: Timer](implicit as: ActorSystem, cfg: ArchivesConfig, F: Effect[F]): F[ArchiveCache[F]] = {
    implicit val retryPolicy: RetryPolicy[F] = cfg.cache.retry.retryPolicy[F]
    val invalidationStrategy                 = StopStrategy.lapsedSinceLastInteraction[State, Command](cfg.cacheInvalidateAfter)

    val evaluate: (State, Command) => F[Either[Unit, State]] = {
      case (None, Write(bundle)) => F.pure(Right(Some(bundle)))
      case (Some(_), _)          => F.pure(Left(())) // It already exists, so we don't want to replace it
    }

    AkkaStateMachine
      .sharded[F]("archives", None, evaluate, invalidationStrategy, cfg.cache.akkaStateMachineConfig, cfg.cache.shards)
      .map(new ArchiveCache[F](_))
  }
} 
Example 14
Source File: package.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg

import akka.http.scaladsl.model.StatusCode
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.server.{MalformedQueryParamRejection, Route}
import cats.Functor
import cats.data.{EitherT, OptionT}
import cats.instances.future._
import ch.epfl.bluebrain.nexus.iam.types.Permission
import ch.epfl.bluebrain.nexus.kg.marshallers.instances._
import ch.epfl.bluebrain.nexus.kg.resources.Rejection.NotFound.notFound
import ch.epfl.bluebrain.nexus.kg.resources.{Ref, Rejection, ResourceV}
import ch.epfl.bluebrain.nexus.kg.routes.OutputFormat.{DOT, Triples}
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.Future

package object routes {

  private[routes] def completeWithFormat(
      fetched: Future[Either[Rejection, (StatusCode, ResourceV)]]
  )(implicit format: NonBinaryOutputFormat): Route =
    completeWithFormat(EitherT(fetched))

  private def completeWithFormat(
      fetched: EitherT[Future, Rejection, (StatusCode, ResourceV)]
  )(implicit format: NonBinaryOutputFormat): Route =
    format match {
      case f: JsonLDOutputFormat =>
        implicit val format = f
        complete(fetched.value)
      case Triples               =>
        implicit val format = Triples
        complete(fetched.map { case (status, resource) => status -> resource.value.graph.ntriples }.value)
      case DOT                   =>
        implicit val format = DOT
        complete(fetched.map { case (status, resource) => status -> resource.value.graph.dot() }.value)
    }

  private[routes] val read: Permission = Permission.unsafe("resources/read")

  private[routes] val schemaError =
    MalformedQueryParamRejection("schema", "The provided schema does not match the schema on the Uri")

  implicit private[routes] class FOptionSyntax[F[_], A](private val fOpt: F[Option[A]]) extends AnyVal {
    def toNotFound(id: AbsoluteIri)(implicit F: Functor[F]): EitherT[F, Rejection, A] =
      OptionT(fOpt).toRight(notFound(Ref(id)))
  }
} 
Example 15
Source File: TracedHttpRoute.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer

import cats.Monad
import cats.data.{Kleisli, OptionT}
import cats.syntax.flatMap._
import cats.syntax.functor._
import dev.profunktor.tracer.Tracer.TraceId
import org.http4s.{HttpRoutes, Request, Response}

object TracedHttpRoute {
  case class TracedRequest[F[_]](traceId: TraceId, request: Request[F])

  def apply[F[_]: Monad: Tracer](
      pf: PartialFunction[TracedRequest[F], F[Response[F]]]
  ): HttpRoutes[F] =
    Kleisli[OptionT[F, ?], Request[F], Response[F]] { req =>
      OptionT {
        Tracer[F]
          .getTraceId(req)
          .map(x => TracedRequest[F](x.getOrElse(TraceId("-")), req))
          .flatMap { tr =>
            val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none))
            rs.value
          }
      }

    }

} 
Example 16
Source File: AuthTracedHttpRoute.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.auth

import cats.Monad
import cats.data.{Kleisli, OptionT}
import cats.syntax.flatMap._
import cats.syntax.functor._
import dev.profunktor.tracer.Tracer
import dev.profunktor.tracer.Tracer.TraceId
import org.http4s.{AuthedRequest, AuthedRoutes, Response}

object AuthTracedHttpRoute {
  case class AuthTracedRequest[F[_], T](traceId: TraceId, request: AuthedRequest[F, T])

  def apply[T, F[_]: Monad: Tracer](
      pf: PartialFunction[AuthTracedRequest[F, T], F[Response[F]]]
  ): AuthedRoutes[T, F] =
    Kleisli[OptionT[F, ?], AuthedRequest[F, T], Response[F]] { req =>
      OptionT {
        Tracer[F]
          .getTraceId(req.req)
          .map(x => AuthTracedRequest[F, T](x.getOrElse(TraceId("-")), req))
          .flatMap { tr =>
            val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none))
            rs.value
          }
      }
    }
} 
Example 17
Source File: SecurityServiceSpec.scala    From cluster-broccoli   with Apache License 2.0 5 votes vote down vote up
package de.frosner.broccoli.services

import cats.data.OptionT
import com.mohiva.play.silhouette.api.LoginInfo
import com.mohiva.play.silhouette.api.services.IdentityService
import com.mohiva.play.silhouette.api.util.Credentials
import com.mohiva.play.silhouette.impl.exceptions.InvalidPasswordException
import com.mohiva.play.silhouette.impl.providers.CredentialsProvider
import de.frosner.broccoli.auth.{Account, AuthConfiguration, AuthMode, Role}
import org.mockito.Mock
import org.specs2.concurrent.ExecutionEnv
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import org.specs2.specification.mutable.ExecutionEnvironment

import scala.concurrent.Future
import scala.concurrent.duration.Duration

class SecurityServiceSpec extends Specification with Mockito with ExecutionEnvironment {

  def configWithAccounts(accounts: Seq[Account]): AuthConfiguration =
    AuthConfiguration(
      mode = AuthMode.Conf,
      session = AuthConfiguration.Session(timeout = Duration(1, "hour"), allowMultiLogin = true),
      cookie = AuthConfiguration.Cookie(secure = true),
      conf = AuthConfiguration.Conf(
        accounts = accounts
          .map(
            a =>
              AuthConfiguration.ConfAccount(
                a.name,
                "",
                a.instanceRegex,
                a.role
            ))
          .toList),
      allowedFailedLogins = 3
    )

  val identityService = mock[IdentityService[Account]]

  val account = Account("frank", "^test.*", Role.Administrator)

  override def is(implicit executionEnv: ExecutionEnv): Any =
    "An authentication check" should {

      "succeed if the credentials provider authenticates" in {
        val login = LoginInfo(CredentialsProvider.ID, account.name)
        val credentials = Credentials(account.name, "pass")

        val credentialsProvider = mock[CredentialsProvider]
        credentialsProvider.authenticate(credentials) returns Future.successful(login)

        SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService)
          .authenticate(credentials) must beSome(login).await
      }

      "fail if the credentials provider fails to authenticate" in {
        val credentials = Credentials(account.name, "pass")

        val credentialsProvider = mock[CredentialsProvider]
        credentialsProvider.authenticate(credentials) returns Future.failed(new InvalidPasswordException("foo"))

        SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService)
          .authenticate(credentials) must beNone.await
      }

      "succeed if the number of failed logins is equal to the allowed ones" in {
        val credentials = Credentials(account.name, "pass")
        val failedCredentials = credentials.copy(password = "foo")
        val login = LoginInfo(CredentialsProvider.ID, credentials.identifier)

        val credentialsProvider = mock[CredentialsProvider]
        credentialsProvider.authenticate(failedCredentials) returns Future.failed(new InvalidPasswordException("foo"))
        credentialsProvider.authenticate(credentials) returns Future.successful(login)

        val service = SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService)
        val failedAttempts = for (attemptNo <- 1 to service.allowedFailedLogins) {
          service.authenticate(failedCredentials) must beNone.await
        }
        service.authenticate(credentials) must beSome(login).await
      }

      "fail if the number of failed logins is greater than the allowed number" in {
        val credentials = Credentials(account.name, "password")
        val failedCredentials = credentials.copy(password = "foo")
        val login = LoginInfo(CredentialsProvider.ID, credentials.identifier)

        val credentialsProvider = mock[CredentialsProvider]
        credentialsProvider.authenticate(failedCredentials) returns Future.failed(new InvalidPasswordException("foo"))
        credentialsProvider.authenticate(credentials) returns Future.successful(login)

        val service = SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService)
        val failedAttempts = for (attemptNo <- 0 to service.allowedFailedLogins) {
          service.authenticate(failedCredentials) must beNone.await
        }
        service.authenticate(credentials) must beNone.await
      }

    }
} 
Example 18
Source File: SecurityController.scala    From cluster-broccoli   with Apache License 2.0 5 votes vote down vote up
package de.frosner.broccoli.controllers

import javax.inject.Inject

import cats.data.{EitherT, OptionT}
import cats.instances.future._
import cats.syntax.either._
import com.mohiva.play.silhouette.api.util.Credentials
import com.mohiva.play.silhouette.impl.providers.CredentialsProvider
import de.frosner.broccoli.services.{SecurityService, WebSocketService}
import jp.t2v.lab.play2.auth.{BroccoliSimpleAuthorization, LoginLogout}
import play.api.{Environment, Logger}
import play.api.cache.CacheApi
import play.api.data.Forms._
import play.api.data._
import play.api.libs.json.Json
import play.api.mvc.{Action, AnyContent, Controller, Results}

import scala.concurrent.Future

case class SecurityController @Inject()(
    override val securityService: SecurityService,
    override val cacheApi: CacheApi,
    override val playEnv: Environment,
    webSocketService: WebSocketService
) extends Controller
    with LoginLogout
    with BroccoliSimpleAuthorization {

  private val log = Logger(getClass)

  import scala.concurrent.ExecutionContext.Implicits.global

  // https://www.playframework.com/documentation/2.5.x/ScalaForms
  val loginForm = Form {
    mapping(
      SecurityController.UsernameFormKey -> text,
      SecurityController.PasswordFormKey -> text
    )(Credentials.apply)(Credentials.unapply)
  }

  def login: Action[AnyContent] = Action.async { implicit request =>
    getSessionId(request).map(id => (id, webSocketService.closeConnections(id))) match {
      case Some((id, true)) => log.info(s"Removing websocket connection of $id due to another login")
      case _                =>
    }
    (for {
      credentials <- EitherT.fromEither[Future](
        loginForm.bindFromRequest().fold(Function.const(Results.BadRequest.asLeft), _.asRight))
      login <- OptionT(securityService.authenticate(credentials)).toRight(Results.Unauthorized)
      result <- EitherT.right(gotoLoginSucceeded(login.providerKey))
      user <- OptionT(resolveUser(login.providerKey)).toRight(Results.Unauthorized)
    } yield {
      val userResult = Results.Ok(Json.toJson(user))
      result.copy(
        header = result.header.copy(
          headers = userResult.header.headers
            .get("Content-Type")
            .map { contentType =>
              result.header.headers.updated("Content-Type", contentType)
            }
            .getOrElse(result.header.headers)
        ),
        body = userResult.body
      )
    }).merge
  }

  def logout = Action.async(parse.empty) { implicit request =>
    gotoLogoutSucceeded.andThen {
      case tryResult =>
        getSessionId(request).map(id => (id, webSocketService.closeConnections(id))) match {
          case Some((id, true))  => log.info(s"Removing websocket connection of $id due to logout")
          case Some((id, false)) => log.info(s"There was no websocket connection for session $id")
          case None              => log.info(s"No session available to logout from")
        }
    }
  }

  def verify = StackAction(parse.empty) { implicit request =>
    Ok(loggedIn.name)
  }

}

object SecurityController {

  val UsernameFormKey = "username"
  val PasswordFormKey = "password"

} 
Example 19
Source File: SecurityService.scala    From cluster-broccoli   with Apache License 2.0 5 votes vote down vote up
package de.frosner.broccoli.services

import javax.inject.{Inject, Singleton}

import cats.data.{EitherT, OptionT}
import cats.instances.future._
import cats.syntax.either._
import com.mohiva.play.silhouette.api.LoginInfo
import com.mohiva.play.silhouette.api.services.IdentityService
import com.mohiva.play.silhouette.api.util.Credentials
import com.mohiva.play.silhouette.impl.exceptions.{IdentityNotFoundException, InvalidPasswordException}
import com.mohiva.play.silhouette.impl.providers.CredentialsProvider
import de.frosner.broccoli.auth.{Account, AuthConfiguration, AuthMode}

import scala.concurrent.{ExecutionContext, Future}

sealed trait LoginError

object LoginError {
  final case object InvalidPassword extends LoginError
  final case object UnknownUser extends LoginError
  final case object Locked extends LoginError
}

@Singleton()
case class SecurityService @Inject()(
    configuration: AuthConfiguration,
    credentialsProvider: CredentialsProvider,
    identityService: IdentityService[Account]
)(implicit ec: ExecutionContext) {

  private val log = play.api.Logger(getClass)

  val sessionTimeoutInSeconds: Int = configuration.session.timeout.toSeconds.toInt

  val allowedFailedLogins: Int = configuration.allowedFailedLogins

  val authMode: AuthMode = configuration.mode

  val cookieSecure: Boolean = configuration.cookie.secure
  val allowMultiLogin: Boolean = configuration.session.allowMultiLogin

  @volatile
  private var failedLoginAttempts: Map[String, Int] = Map.empty

  
  def authenticate(credentials: Credentials): Future[Option[LoginInfo]] =
    EitherT
      .rightT(credentials)
      .ensure(LoginError.Locked)(c => failedLoginAttempts.getOrElse(c.identifier, 0) <= allowedFailedLogins)
      .flatMapF(credentialsProvider.authenticate(_).map(_.asRight).recover {
        case _: InvalidPasswordException  => LoginError.InvalidPassword.asLeft
        case _: IdentityNotFoundException => LoginError.UnknownUser.asLeft
      })
      .leftMap { error =>
        // Login if an account was locked
        val attempts = failedLoginAttempts.getOrElse(credentials.identifier, 0)
        if (error == LoginError.Locked) {
          log.warn(
            s"Credentials for '${credentials.identifier}' exceeded the allowed number of failed logins: " +
              s"$allowedFailedLogins (has $attempts)")
        }
        // Track the failed attempt
        failedLoginAttempts = failedLoginAttempts.updated(credentials.identifier, attempts + 1)
        error
      }
      .toOption
      .value
} 
Example 20
Source File: UserRepositoryInMemoryInterpreter.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore
package infrastructure.repository.inmemory

import java.util.Random

import cats.implicits._
import cats.Applicative
import cats.data.OptionT
import domain.users.{User, UserRepositoryAlgebra}
import tsec.authentication.IdentityStore

import scala.collection.concurrent.TrieMap

class UserRepositoryInMemoryInterpreter[F[_]: Applicative]
    extends UserRepositoryAlgebra[F]
    with IdentityStore[F, Long, User] {
  private val cache = new TrieMap[Long, User]

  private val random = new Random

  def create(user: User): F[User] = {
    val id = random.nextLong
    val toSave = user.copy(id = id.some)
    cache += (id -> toSave)
    toSave.pure[F]
  }

  def update(user: User): OptionT[F, User] = OptionT {
    user.id.traverse { id =>
      cache.update(id, user)
      user.pure[F]
    }
  }

  def get(id: Long): OptionT[F, User] =
    OptionT.fromOption(cache.get(id))

  def delete(id: Long): OptionT[F, User] =
    OptionT.fromOption(cache.remove(id))

  def findByUserName(userName: String): OptionT[F, User] =
    OptionT.fromOption(cache.values.find(u => u.userName == userName))

  def list(pageSize: Int, offset: Int): F[List[User]] =
    cache.values.toList.sortBy(_.lastName).slice(offset, offset + pageSize).pure[F]

  def deleteByUserName(userName: String): OptionT[F, User] =
    OptionT.fromOption(
      for {
        user <- cache.values.find(u => u.userName == userName)
        removed <- cache.remove(user.id.get)
      } yield removed,
    )
}

object UserRepositoryInMemoryInterpreter {
  def apply[F[_]: Applicative]() =
    new UserRepositoryInMemoryInterpreter[F]
} 
Example 21
Source File: CorrelationIdMiddleware.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.middleware

import java.util.UUID

import cats.data.{Kleisli, OptionT}
import cats.effect.Sync
import cats.syntax.functor._
import com.avast.sst.http4s.server.middleware.CorrelationIdMiddleware.CorrelationId
import io.chrisdavenport.vault.Key
import org.http4s.util.CaseInsensitiveString
import org.http4s.{Header, HttpRoutes, Request, Response}
import org.slf4j.LoggerFactory


class CorrelationIdMiddleware[F[_]: Sync](
    correlationIdHeaderName: CaseInsensitiveString,
    attributeKey: Key[CorrelationId],
    generator: () => String
) {

  private val logger = LoggerFactory.getLogger(this.getClass)

  private val F = Sync[F]

  def wrap(routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli[OptionT[F, *], Request[F], Response[F]] { request =>
      request.headers.get(correlationIdHeaderName) match {
        case Some(header) =>
          val requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(header.value))
          routes(requestWithAttribute).map(r => r.withHeaders(r.headers.put(header)))
        case None =>
          for {
            newCorrelationId <- OptionT.liftF(F.delay(generator()))
            _ <- log(newCorrelationId)
            requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(newCorrelationId))
            response <- routes(requestWithAttribute)
          } yield response.withHeaders(response.headers.put(Header(correlationIdHeaderName.value, newCorrelationId)))
      }
    }

  def retrieveCorrelationId(request: Request[F]): Option[CorrelationId] = request.attributes.lookup(attributeKey)

  private def log(newCorrelationId: String) = {
    OptionT.liftF {
      F.delay {
        if (logger.isDebugEnabled()) {
          logger.debug(s"Generated new correlation ID: $newCorrelationId")
        }
      }
    }
  }
}

object CorrelationIdMiddleware {

  final case class CorrelationId(value: String) extends AnyVal

  @SuppressWarnings(Array("scalafix:Disable.toString"))
  def default[F[_]: Sync]: F[CorrelationIdMiddleware[F]] = {
    Key.newKey[F, CorrelationId].map { attributeKey =>
      new CorrelationIdMiddleware(CaseInsensitiveString("Correlation-ID"), attributeKey, () => UUID.randomUUID().toString)
    }
  }

} 
Example 22
Source File: Streamable.scala    From AckCord   with MIT License 5 votes vote down vote up
package ackcord.util

import scala.concurrent.Future

import akka.NotUsed
import akka.stream.scaladsl.Source
import cats.{Foldable, Id}
import cats.data.OptionT


trait Streamable[F[_]] {
  def toSource[A](fa: F[A]): Source[A, NotUsed]

  def optionToSource[A](opt: OptionT[F, A]): Source[A, NotUsed] = toSource(opt.value).mapConcat(_.toList)
}
object Streamable {
  def apply[F[_]](implicit F: Streamable[F]): Streamable[F] = F

  type OptionTRequest[A] = OptionT[Future, A]

  implicit val idStreamable: Streamable[Id] = new Streamable[Id] {
    override def toSource[A](fa: Id[A]): Source[A, NotUsed]                 = Source.single(fa)
    override def optionToSource[A](opt: OptionT[Id, A]): Source[A, NotUsed] = Source(opt.value.toList)
  }

  implicit val futureStreamable: Streamable[Future] = new Streamable[Future] {
    override def toSource[A](fa: Future[A]): Source[A, NotUsed] = Source.future(fa)
  }

  implicit def futureFoldableStreamable[F[_]: Foldable]: Streamable[λ[A => Future[F[A]]]] =
    new Streamable[λ[A => Future[F[A]]]] {
      override def toSource[A](fa: Future[F[A]]): Source[A, NotUsed] = {
        import cats.syntax.all._
        Source.future(fa).mapConcat(_.toList)
      }
    }

  implicit val futureOptionTStreamable: Streamable[OptionT[Future, *]] = new Streamable[OptionT[Future, *]] {
    override def toSource[A](fa: OptionT[Future, A]): Source[A, NotUsed] = Source.future(fa.value).mapConcat(_.toList)
  }

  implicit val sourceStreamable: Streamable[Source[*, NotUsed]] = new Streamable[Source[?, NotUsed]] {
    override def toSource[A](fa: Source[A, NotUsed]): Source[A, NotUsed] = fa
  }
} 
Example 23
Source File: MetaMapper.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.lang.contract.meta

import cats.implicits._
import cats.data.OptionT
import com.wavesplatform.lang.contract.DApp
import com.wavesplatform.lang.v1.compiler.Types.FINAL
import com.wavesplatform.protobuf.dapp.DAppMeta

object MetaMapper {
  def toProto[V <: MetaVersion](version: V)(data: List[List[FINAL]]): Either[String, DAppMeta] =
    version.strategy.toProto(data)

  def dicFromProto(dApp: DApp): Either[String, ParsedMeta] =
    extractMeta(dApp).value.map(opt => ParsedMeta(dApp.meta.version, opt))

  private def extractMeta(dApp: DApp) =
    for {
      version <- OptionT(resolveVersion(dApp.meta.version))
      data    <- OptionT.liftF(version.strategy.fromProto(dApp.meta))
    } yield data

  private def resolveVersion(version: Int): Either[String, Option[MetaVersion]] =
    version match {
      case 0          => Right(None)
      case 1          => Right(Some(V1))
      case 2          => Right(Some(V2))
      case n if n > 0 => Left(s"Unsupported meta version $n")
      case n          => Left(s"Illegal meta version $n, expected positive value")
    }
} 
Example 24
Source File: ArchiveCache.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.archives

import akka.actor.{ActorSystem, NotInfluenceReceiveTimeout}
import cats.Monad
import cats.data.OptionT
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.kg.archives.ArchiveCache._
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.resources.ResId
import ch.epfl.bluebrain.nexus.sourcing.StateMachine
import ch.epfl.bluebrain.nexus.sourcing.akka.StopStrategy
import ch.epfl.bluebrain.nexus.sourcing.akka.statemachine.AkkaStateMachine
import retry.RetryPolicy

class ArchiveCache[F[_]: Monad](ref: StateMachine[F, String, State, Command, Unit]) {

  
  def put(value: Archive): OptionT[F, Archive] =
    OptionT(ref.evaluate(value.resId.show, Write(value)).map(_.toOption.flatten))

}

object ArchiveCache {

  private[archives] type State   = Option[Archive]
  private[archives] type Command = Write
  private[archives] final case class Write(bundle: Archive) extends NotInfluenceReceiveTimeout

  final def apply[F[_]: Timer](implicit as: ActorSystem, cfg: ArchivesConfig, F: Effect[F]): F[ArchiveCache[F]] = {
    implicit val retryPolicy: RetryPolicy[F] = cfg.cache.retry.retryPolicy[F]
    val invalidationStrategy                 = StopStrategy.lapsedSinceLastInteraction[State, Command](cfg.cacheInvalidateAfter)

    val evaluate: (State, Command) => F[Either[Unit, State]] = {
      case (None, Write(bundle)) => F.pure(Right(Some(bundle)))
      case (Some(_), _)          => F.pure(Left(())) // It already exists, so we don't want to replace it
    }

    AkkaStateMachine
      .sharded[F]("archives", None, evaluate, invalidationStrategy, cfg.cache.akkaStateMachineConfig, cfg.cache.shards)
      .map(new ArchiveCache[F](_))
  }
} 
Example 25
Source File: package.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg

import akka.http.scaladsl.model.StatusCode
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.server.{MalformedQueryParamRejection, Route}
import cats.Functor
import cats.data.{EitherT, OptionT}
import cats.instances.future._
import ch.epfl.bluebrain.nexus.iam.client.types._
import ch.epfl.bluebrain.nexus.kg.marshallers.instances._
import ch.epfl.bluebrain.nexus.kg.resources.Rejection.NotFound.notFound
import ch.epfl.bluebrain.nexus.kg.resources.{Ref, Rejection, ResourceV}
import ch.epfl.bluebrain.nexus.kg.routes.OutputFormat.{DOT, Triples}
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.Future

package object routes {

  private[routes] def completeWithFormat(
      fetched: Future[Either[Rejection, (StatusCode, ResourceV)]]
  )(implicit format: NonBinaryOutputFormat): Route =
    completeWithFormat(EitherT(fetched))

  private def completeWithFormat(
      fetched: EitherT[Future, Rejection, (StatusCode, ResourceV)]
  )(implicit format: NonBinaryOutputFormat): Route =
    format match {
      case f: JsonLDOutputFormat =>
        implicit val format = f
        complete(fetched.value)
      case Triples =>
        implicit val format = Triples
        complete(fetched.map { case (status, resource) => status -> resource.value.graph.ntriples }.value)
      case DOT =>
        implicit val format = DOT
        complete(fetched.map { case (status, resource) => status -> resource.value.graph.dot() }.value)
    }

  private[routes] val read: Permission = Permission.unsafe("resources/read")

  private[routes] val schemaError =
    MalformedQueryParamRejection("schema", "The provided schema does not match the schema on the Uri")

  private[routes] implicit class FOptionSyntax[F[_], A](private val fOpt: F[Option[A]]) extends AnyVal {
    def toNotFound(id: AbsoluteIri)(implicit F: Functor[F]): EitherT[F, Rejection, A] =
      OptionT(fOpt).toRight(notFound(Ref(id)))
  }
} 
Example 26
Source File: Http4sUtils.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.http

import cats.{Applicative, Monad}
import cats.data.{Kleisli, OptionT}
import cats.effect.IO
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.http4s.server.AuthMiddleware
import org.http4s.{EntityBody, Request}

import scala.concurrent.Await
import scala.concurrent.duration.Duration

object Http4sUtils {

  private def authUser[F[_]](implicit F: Applicative[F]): Kleisli[OptionT[F, ?], Request[F], String] =
    Kleisli(_ => OptionT.liftF(F.pure("access_token")))

  def middleware[F[_]: Monad]: AuthMiddleware[F, String] = AuthMiddleware.apply[F, String](authUser)

  val taskMiddleware: AuthMiddleware[Task, String] = middleware[Task]
  val ioMiddleware: AuthMiddleware[IO, String] = middleware[IO]

  implicit class ByteVector2String(body: EntityBody[IO]) {
    def asString: String = {
      val array = body.compile.toVector.unsafeRunSync().toArray
      new String(array.map(_.toChar))
    }
  }

  implicit class ByteVector2StringTask(body: EntityBody[Task]) {
    def asString: String = {
      val array = Await.result(body.compile.toVector.runAsync, Duration.Inf).toArray
      new String(array.map(_.toChar))
    }
  }

} 
Example 27
Source File: JwtTokenAuthMiddleware.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.http.auth

import cats.data.{EitherT, Kleisli, OptionT}
import cats.effect.Sync
import cats.syntax.applicativeError._
import cats.syntax.functor._
import com.smartbackpackerapp.http.auth.JwtTokenAuthMiddleware.AuthConfig
import org.http4s.Credentials.Token
import org.http4s.dsl.Http4sDsl
import org.http4s.{AuthScheme, AuthedService, Request}
import org.http4s.headers.Authorization
import org.http4s.server.AuthMiddleware
import tsec.jws.mac.JWTMac
import tsec.mac.imports._

object JwtTokenAuthMiddleware {
  def apply[F[_] : Sync](apiToken: Option[String]): F[AuthMiddleware[F, String]] =
    new Middleware[F](apiToken).middleware

  case class AuthConfig(jwtKey: MacSigningKey[HMACSHA256])
}

class Middleware[F[_]](apiToken: Option[String])(implicit F: Sync[F]) {

  private val ifEmpty = F.raiseError[AuthMiddleware[F, String]](new Exception("Api Token not found"))

  private def generateJwtKey(token: String): F[MacSigningKey[HMACSHA256]] = {
    F.catchNonFatal(HMACSHA256.buildKeyUnsafe(token.getBytes))
  }

  val middleware: F[AuthMiddleware[F, String]] = apiToken.fold(ifEmpty) { token =>
    generateJwtKey(token).map { jwtKey =>
      val config = AuthConfig(jwtKey)
      new JwtTokenAuthMiddleware[F](config).middleware
    }
  }
}

class JwtTokenAuthMiddleware[F[_] : Sync](config: AuthConfig) extends Http4sDsl[F] {

  private val onFailure: AuthedService[String, F] =
    Kleisli(req => OptionT.liftF(Forbidden(req.authInfo)))

  private def bearerTokenFromRequest(request: Request[F]): OptionT[F, String] =
    OptionT.fromOption[F] {
      request.headers.get(Authorization).collect {
        case Authorization(Token(AuthScheme.Bearer, token)) => token
      }
    }

  private def verifyToken(request: Request[F],
                          jwtKey: MacSigningKey[HMACSHA256]): OptionT[F, String] =
    for {
      token       <- bearerTokenFromRequest(request)
      verified    <- OptionT.liftF(JWTMac.verifyAndParse[F, HMACSHA256](token, jwtKey))
      accessToken <- OptionT.fromOption[F](verified.body.subject)
    } yield accessToken

  private def authUser(jwtKey: MacSigningKey[HMACSHA256]): Kleisli[F, Request[F], Either[String, String]] =
    Kleisli { request =>
      verifyToken(request, jwtKey).value.map { option =>
        Either.cond[String, String](option.isDefined, option.get, "Unable to authorize token")
      }.recoverWith {
        case MacVerificationError(msg) => EitherT.leftT(msg).value
      }
    }

  def middleware: AuthMiddleware[F, String] =
    AuthMiddleware(authUser(config.jwtKey), onFailure)

} 
Example 28
Source File: HttpMetricsMiddleware.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.http.metrics

import cats.data.{Kleisli, OptionT}
import cats.effect.Sync
import cats.syntax.flatMap._
import cats.syntax.functor._
import com.codahale.metrics._
import com.smartbackpackerapp.common.Log
import com.smartbackpackerapp.http.ApiVersion
import org.http4s.AuthedService
import org.http4s.Uri.Path

object HttpMetricsMiddleware {

  def apply[F[_]](registry: MetricRegistry,
                  service: AuthedService[String, F])
                 (implicit F: Sync[F], L: Log[F]): AuthedService[String, F] = {

    Kleisli { req =>
      OptionT.liftF(F.delay(System.nanoTime())).flatMap { start =>
        service(req).semiflatMap { response =>
          HttpMetrics.parse(req.req.uri.path).fold(F.delay(response)) { path =>
            for {
              _    <- F.delay(registry.meter(s"requests-$path").mark())
              _    <- if (response.status.isSuccess) F.delay(registry.meter(s"success-$path").mark())
                      else F.delay(registry.meter(s"failure-${response.status.code}-$path").mark())
              time <- F.delay((System.nanoTime() - start) / 1000000)
              _    <- F.delay(registry.histogram(s"response-time-$path").update(time))
              _    <- L.info(s"HTTP Response Time: $time ms")
            } yield response
          }
        }
      }
    }
  }

}

object HttpMetrics {

  def parse(path: Path): Option[String] = {
    if (path.contains("/traveling")) Some(s"$ApiVersion-traveling")
    else if (path.contains("/airlines")) Some(s"$ApiVersion-airlines")
    else if (path.contains("/ranking")) Some(s"$ApiVersion-ranking")
    else if (path.contains("/health")) Some(s"$ApiVersion-health")
    else if (path.contains("/countries")) Some(s"$ApiVersion-countries")
    else None
  }

} 
Example 29
Source File: CollectionsServiceSpec.scala    From franklin   with Apache License 2.0 5 votes vote down vote up
package com.azavea.franklin.api.services

import cats.data.OptionT
import cats.effect.IO
import cats.implicits._
import com.azavea.franklin.Generators
import com.azavea.franklin.api.{TestClient, TestServices}
import com.azavea.franklin.database.TestDatabaseSpec
import com.azavea.franklin.datamodel.CollectionsResponse
import com.azavea.stac4s.StacCollection
import com.azavea.stac4s.testing._
import org.http4s.circe.CirceEntityDecoder._
import org.http4s.{Method, Request, Uri}
import org.specs2.{ScalaCheck, Specification}

import java.net.URLEncoder
import java.nio.charset.StandardCharsets

class CollectionsServiceSpec
    extends Specification
    with ScalaCheck
    with TestDatabaseSpec
    with Generators {
  def is = s2"""
  This specification verifies that the collections service can run without crashing

  The collections service should:
    - create and delete collections $createDeleteCollectionExpectation
    - list collections              $listCollectionsExpectation
    - get collections by id         $getCollectionsExpectation
"""

  val testServices: TestServices[IO] = new TestServices[IO](transactor)

  val testClient: TestClient[IO] =
    new TestClient[IO](testServices.collectionsService, testServices.collectionItemsService)

  def listCollectionsExpectation = prop {
    (stacCollectionA: StacCollection, stacCollectionB: StacCollection) =>
      {
        val listIO = (
          testClient.getCollectionResource(stacCollectionA),
          testClient.getCollectionResource(stacCollectionB)
        ).tupled use { _ =>
          val request = Request[IO](method = Method.GET, Uri.unsafeFromString(s"/collections"))
          (for {
            resp    <- testServices.collectionsService.routes.run(request)
            decoded <- OptionT.liftF { resp.as[CollectionsResponse] }
          } yield decoded).value
        }

        val result = listIO.unsafeRunSync.get.collections map { _.id }

        (result must contain(stacCollectionA.id)) and (result must contain(stacCollectionB.id))
      }
  }

  def getCollectionsExpectation = prop { (stacCollection: StacCollection) =>
    val fetchIO =
      testClient.getCollectionResource(stacCollection) use { collection =>
        val encodedId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString)
        val request =
          Request[IO](method = Method.GET, Uri.unsafeFromString(s"/collections/$encodedId"))
        (for {
          resp    <- testServices.collectionsService.routes.run(request)
          decoded <- OptionT.liftF { resp.as[StacCollection] }
        } yield (decoded, collection)).value
      }

    val (fetched, inserted) = fetchIO.unsafeRunSync.get

    fetched must beTypedEqualTo(inserted)
  }

  // since creation / deletion is a part of the collection resource, and accurate creation is checked
  // in getCollectionsExpectation, this test just makes sure that if other tests are failing, it's
  // not because create/delete are broken
  def createDeleteCollectionExpectation = prop { (stacCollection: StacCollection) =>
    (testClient
      .getCollectionResource(stacCollection) use { _ => IO.unit }).unsafeRunSync must beTypedEqualTo(
      ()
    )
  }

} 
Example 30
Source File: DoobieUserRepositoryInterpreter.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore
package infrastructure.repository.doobie

import cats.data.OptionT
import cats.effect.Bracket
import cats.implicits._
import doobie._
import doobie.implicits._
import io.circe.parser.decode
import io.circe.syntax._
import domain.users.{Role, User, UserRepositoryAlgebra}
import io.github.pauljamescleary.petstore.infrastructure.repository.doobie.SQLPagination._
import tsec.authentication.IdentityStore

private object UserSQL {
  // H2 does not support JSON data type.
  implicit val roleMeta: Meta[Role] =
    Meta[String].imap(decode[Role](_).leftMap(throw _).merge)(_.asJson.toString)

  def insert(user: User): Update0 = sql"""
    INSERT INTO USERS (USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ROLE)
    VALUES (${user.userName}, ${user.firstName}, ${user.lastName}, ${user.email}, ${user.hash}, ${user.phone}, ${user.role})
  """.update

  def update(user: User, id: Long): Update0 = sql"""
    UPDATE USERS
    SET FIRST_NAME = ${user.firstName}, LAST_NAME = ${user.lastName},
        EMAIL = ${user.email}, HASH = ${user.hash}, PHONE = ${user.phone}, ROLE = ${user.role}
    WHERE ID = $id
  """.update

  def select(userId: Long): Query0[User] = sql"""
    SELECT USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ID, ROLE
    FROM USERS
    WHERE ID = $userId
  """.query

  def byUserName(userName: String): Query0[User] = sql"""
    SELECT USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ID, ROLE
    FROM USERS
    WHERE USER_NAME = $userName
  """.query[User]

  def delete(userId: Long): Update0 = sql"""
    DELETE FROM USERS WHERE ID = $userId
  """.update

  val selectAll: Query0[User] = sql"""
    SELECT USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ID, ROLE
    FROM USERS
  """.query
}

class DoobieUserRepositoryInterpreter[F[_]: Bracket[?[_], Throwable]](val xa: Transactor[F])
    extends UserRepositoryAlgebra[F]
    with IdentityStore[F, Long, User] { self =>
  import UserSQL._

  def create(user: User): F[User] =
    insert(user).withUniqueGeneratedKeys[Long]("ID").map(id => user.copy(id = id.some)).transact(xa)

  def update(user: User): OptionT[F, User] =
    OptionT.fromOption[F](user.id).semiflatMap { id =>
      UserSQL.update(user, id).run.transact(xa).as(user)
    }

  def get(userId: Long): OptionT[F, User] = OptionT(select(userId).option.transact(xa))

  def findByUserName(userName: String): OptionT[F, User] =
    OptionT(byUserName(userName).option.transact(xa))

  def delete(userId: Long): OptionT[F, User] =
    get(userId).semiflatMap(user => UserSQL.delete(userId).run.transact(xa).as(user))

  def deleteByUserName(userName: String): OptionT[F, User] =
    findByUserName(userName).mapFilter(_.id).flatMap(delete)

  def list(pageSize: Int, offset: Int): F[List[User]] =
    paginate(pageSize, offset)(selectAll).to[List].transact(xa)
}

object DoobieUserRepositoryInterpreter {
  def apply[F[_]: Bracket[?[_], Throwable]](xa: Transactor[F]): DoobieUserRepositoryInterpreter[F] =
    new DoobieUserRepositoryInterpreter(xa)
} 
Example 31
Source File: DoobieOrderRepositoryInterpreter.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore
package infrastructure.repository.doobie

import cats.data.OptionT
import cats.effect.Bracket
import cats.implicits._
import doobie._
import doobie.implicits._
import doobie.implicits.legacy.instant._
import domain.orders.{Order, OrderRepositoryAlgebra, OrderStatus}

private object OrderSQL {
  
  implicit val StatusMeta: Meta[OrderStatus] =
    Meta[String].imap(OrderStatus.withName)(_.entryName)

  def select(orderId: Long): Query0[Order] = sql"""
    SELECT PET_ID, SHIP_DATE, STATUS, COMPLETE, ID, USER_ID
    FROM ORDERS
    WHERE ID = $orderId
  """.query[Order]

  def insert(order: Order): Update0 = sql"""
    INSERT INTO ORDERS (PET_ID, SHIP_DATE, STATUS, COMPLETE, USER_ID)
    VALUES (${order.petId}, ${order.shipDate}, ${order.status}, ${order.complete}, ${order.userId.get})
  """.update

  def delete(orderId: Long): Update0 = sql"""
    DELETE FROM ORDERS
    WHERE ID = $orderId
  """.update
}

class DoobieOrderRepositoryInterpreter[F[_]: Bracket[?[_], Throwable]](val xa: Transactor[F])
    extends OrderRepositoryAlgebra[F] {
  import OrderSQL._

  def create(order: Order): F[Order] =
    insert(order)
      .withUniqueGeneratedKeys[Long]("ID")
      .map(id => order.copy(id = id.some))
      .transact(xa)

  def get(orderId: Long): F[Option[Order]] =
    OrderSQL.select(orderId).option.transact(xa)

  def delete(orderId: Long): F[Option[Order]] =
    OptionT(get(orderId))
      .semiflatMap(order => OrderSQL.delete(orderId).run.transact(xa).as(order))
      .value
}

object DoobieOrderRepositoryInterpreter {
  def apply[F[_]: Bracket[?[_], Throwable]](
      xa: Transactor[F],
  ): DoobieOrderRepositoryInterpreter[F] =
    new DoobieOrderRepositoryInterpreter(xa)
} 
Example 32
Source File: MessageHandler.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.services

import java.time.LocalDateTime

import cats.data.OptionT
import cats.effect.IO
import gospeak.core.ApplicationConf
import gospeak.core.domain.Group
import gospeak.core.domain.Group.Settings.Action
import gospeak.core.domain.Group.Settings.Action.Trigger
import gospeak.core.domain.messages.Message
import gospeak.core.domain.utils.Constants
import gospeak.core.services.email.EmailSrv
import gospeak.core.services.slack.SlackSrv
import gospeak.core.services.storage.{OrgaGroupRepo, OrgaGroupSettingsRepo}
import gospeak.core.services.twitter.{Tweets, TwitterSrv}
import gospeak.libs.scala.Extensions._
import gospeak.libs.scala.domain.{CustomException, EmailAddress}
import gospeak.web.services.MessageSrv._
import io.circe.Json
import org.slf4j.LoggerFactory

import scala.util.control.NonFatal

class MessageHandler(appConf: ApplicationConf,
                     groupRepo: OrgaGroupRepo,
                     groupSettingsRepo: OrgaGroupSettingsRepo,
                     emailSrv: EmailSrv,
                     slackSrv: SlackSrv,
                     twitterSrv: TwitterSrv) {
  private val logger = LoggerFactory.getLogger(this.getClass)

  def groupActionHandler(msg: Message): IO[Unit] = (msg match {
    case m: Message.GroupMessage => handleGroupAction(m.group.slug, m, eMessage(m))
    case _ => IO.pure(0)
  }).map(_ => ()).recover { case NonFatal(_) => () }

  def gospeakHandler(msg: Message): IO[Unit] = (msg match {
    case m: Message.ExternalCfpCreated => gospeakTwitt(m)
    case _ => IO.pure(0)
  }).map(_ => ()).recover { case NonFatal(_) => () }

  def logHandler(msg: Message): IO[Unit] = IO.pure(logger.info(s"Message sent: $msg"))

  private def handleGroupAction(group: Group.Slug, msg: Message.GroupMessage, data: Json): IO[Int] = (for {
    groupElt <- OptionT(groupRepo.find(group))
    actions <- OptionT.liftF(groupSettingsRepo.findActions(groupElt.id))
    accounts <- OptionT.liftF(groupSettingsRepo.findAccounts(groupElt.id))
    actionsToExec = Trigger.all.filter(_.message == msg.ref).flatMap(actions.getOrElse(_, Seq()))
    results <- OptionT.liftF(actionsToExec.map(execGroupAction(accounts, _, data)).sequence)
  } yield results.length).value.map(_.getOrElse(0))

  private def execGroupAction(accounts: Group.Settings.Accounts, action: Action, data: Json): IO[Unit] = action match {
    case email: Action.Email =>
      (for {
        to <- email.to.render(data).left.map(e => CustomException(e.message)).flatMap(EmailAddress.from).map(EmailAddress.Contact(_))
        subject <- email.subject.render(data).left.map(e => CustomException(e.message))
        content <- email.content.render(data).map(_.toHtml).leftMap(e => CustomException(e.message))
      } yield emailSrv.send(EmailSrv.Email(
        from = Constants.Gospeak.noreplyEmail,
        to = Seq(to),
        subject = subject,
        content = EmailSrv.HtmlContent(content.value)
      ))).toIO.flatMap(identity).map(_ => ())
    case Action.Slack(slack) =>
      accounts.slack.map(slackSrv.exec(slack, data, _, appConf.aesKey)).getOrElse(IO.raiseError(CustomException("No credentials for Slack")))
  }

  private def gospeakTwitt(msg: Message.ExternalCfpCreated): IO[Int] = {
    if (msg.cfp.isActive(LocalDateTime.now())) {
      twitterSrv.tweet(Tweets.externalCfpCreated(msg)).map(_ => 1)
    } else {
      IO.pure(0)
    }
  }
} 
Example 33
Source File: AuthRepo.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.auth.services

import cats.data.OptionT
import com.mohiva.play.silhouette.api.LoginInfo
import com.mohiva.play.silhouette.api.services.IdentityService
import com.mohiva.play.silhouette.api.util.PasswordInfo
import com.mohiva.play.silhouette.persistence.daos.DelegableAuthInfoDAO
import gospeak.core.domain.User
import gospeak.core.domain.User._
import gospeak.core.services.storage.{AuthGroupRepo, AuthUserRepo}
import gospeak.web.auth.domain.AuthUser
import gospeak.libs.scala.domain.Done

import scala.concurrent.Future
import scala.reflect.ClassTag

// TODO merge it with AuthSrv
class AuthRepo(userRepo: AuthUserRepo, groupRepo: AuthGroupRepo) extends DelegableAuthInfoDAO[PasswordInfo] with IdentityService[AuthUser] {
  override val classTag: ClassTag[PasswordInfo] = scala.reflect.classTag[PasswordInfo]

  override def retrieve(loginInfo: LoginInfo): Future[Option[AuthUser]] = (for {
    user <- OptionT(userRepo.find(toDomain(loginInfo)))
    groups <- OptionT.liftF(groupRepo.list(user.id))
  } yield AuthUser(loginInfo, user, groups)).value.unsafeToFuture()

  override def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] =
    userRepo.findCredentials(toDomain(loginInfo)).map(_.map(toSilhouette)).unsafeToFuture()

  override def add(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] =
    userRepo.createCredentials(toDomain(loginInfo, authInfo)).map(toSilhouette).unsafeToFuture()

  override def update(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] =
    userRepo.editCredentials(toDomain(loginInfo))(toDomain(authInfo)).map(_ => authInfo).unsafeToFuture()

  // add or update
  override def save(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] =
    userRepo.findCredentials(toDomain(loginInfo)).flatMap { opt =>
      opt.map(_ => userRepo.editCredentials(toDomain(loginInfo))(toDomain(authInfo)))
        .getOrElse(userRepo.createCredentials(toDomain(loginInfo, authInfo)).map(_ => Done))
    }.map(_ => authInfo).unsafeToFuture()

  override def remove(loginInfo: LoginInfo): Future[Unit] =
    userRepo.removeCredentials(toDomain(loginInfo)).map(_ => ()).unsafeToFuture()

  private def toDomain(loginInfo: LoginInfo): User.Login = User.Login(ProviderId(loginInfo.providerID), ProviderKey(loginInfo.providerKey))

  private def toDomain(authInfo: PasswordInfo): User.Password = User.Password(Hasher(authInfo.hasher), PasswordValue(authInfo.password), authInfo.salt.map(Salt))

  private def toDomain(loginInfo: LoginInfo, authInfo: PasswordInfo): User.Credentials = User.Credentials(toDomain(loginInfo), toDomain(authInfo))

  private def toSilhouette(p: User.Password): PasswordInfo = PasswordInfo(p.hasher.value, p.password.value, p.salt.map(_.value))

  private def toSilhouette(c: User.Credentials): PasswordInfo = toSilhouette(c.pass)
} 
Example 34
Source File: ApiCfpCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.api.orga

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Cfp, Group}
import gospeak.core.services.storage.{OrgaCfpRepo, OrgaGroupRepo, OrgaUserRepo}
import gospeak.web.AppConf
import gospeak.web.api.domain.ApiCfp
import gospeak.web.api.domain.utils.ApiResult
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.utils.ApiCtrl
import gospeak.libs.scala.domain.Page
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class ApiCfpCtrl(cc: ControllerComponents,
                 silhouette: Silhouette[CookieEnv],
                 conf: AppConf,
                 userRepo: OrgaUserRepo,
                 val groupRepo: OrgaGroupRepo,
                 cfpRepo: OrgaCfpRepo) extends ApiCtrl(cc, silhouette, conf) with ApiCtrl.OrgaAction {
  def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiCfp.Orga]](group) { implicit req =>
    for {
      cfps <- cfpRepo.list(params)
      users <- userRepo.list(cfps.items.flatMap(_.users))
    } yield ApiResult.of(cfps, (c: Cfp) => ApiCfp.orga(c, users))
  }

  def detail(group: Group.Slug, cfp: Cfp.Slug): Action[AnyContent] = OrgaAction[ApiCfp.Orga](group) { implicit req =>
    (for {
      cfpElt <- OptionT(cfpRepo.find(cfp))
      users <- OptionT.liftF(userRepo.list(cfpElt.users))
      res = ApiResult.of(ApiCfp.orga(cfpElt, users))
    } yield res).value.map(_.getOrElse(cfpNotFound(cfp)))
  }

  // TODO def userRatings(group: Group.Slug, cfp: Cfp.Slug)
} 
Example 35
Source File: ApiEventCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.api.orga

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Event, Group}
import gospeak.core.services.storage.{OrgaEventRepo, OrgaGroupRepo, OrgaProposalRepo, OrgaUserRepo}
import gospeak.web.AppConf
import gospeak.web.api.domain.ApiEvent
import gospeak.web.api.domain.utils.ApiResult
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.utils.ApiCtrl
import gospeak.libs.scala.domain.Page
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class ApiEventCtrl(cc: ControllerComponents,
                   silhouette: Silhouette[CookieEnv],
                   conf: AppConf,
                   userRepo: OrgaUserRepo,
                   val groupRepo: OrgaGroupRepo,
                   eventRepo: OrgaEventRepo,
                   proposalRepo: OrgaProposalRepo) extends ApiCtrl(cc, silhouette, conf) with ApiCtrl.OrgaAction {
  def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiEvent.Orga]](group) { implicit req =>
    for {
      events <- eventRepo.listFull(params)
      proposals <- proposalRepo.list(events.items.flatMap(_.talks))
      users <- userRepo.list(events.items.flatMap(_.users) ++ proposals.flatMap(_.users))
    } yield ApiResult.of(events, (e: Event.Full) => ApiEvent.orga(e, proposals, users))
  }

  def detail(group: Group.Slug, event: Event.Slug): Action[AnyContent] = OrgaAction[ApiEvent.Orga](group) { implicit req =>
    (for {
      eventElt <- OptionT(eventRepo.findFull(event))
      proposals <- OptionT.liftF(proposalRepo.list(eventElt.talks))
      users <- OptionT.liftF(userRepo.list(eventElt.users ++ proposals.flatMap(_.users)))
      res = ApiResult.of(ApiEvent.orga(eventElt, proposals, users))
    } yield res).value.map(_.getOrElse(eventNotFound(group, event)))
  }
} 
Example 36
Source File: ApiProposalCtrl.scala    From gospeak   with Apache License 2.0 5 votes vote down vote up
package gospeak.web.api.orga

import cats.data.OptionT
import com.mohiva.play.silhouette.api.Silhouette
import gospeak.core.domain.{Cfp, Group, Proposal}
import gospeak.core.services.storage._
import gospeak.web.AppConf
import gospeak.web.api.domain.utils.ApiResult
import gospeak.web.api.domain.{ApiComment, ApiProposal}
import gospeak.web.auth.domain.CookieEnv
import gospeak.web.utils.ApiCtrl
import gospeak.libs.scala.domain.Page
import play.api.mvc.{Action, AnyContent, ControllerComponents}

class ApiProposalCtrl(cc: ControllerComponents,
                      silhouette: Silhouette[CookieEnv],
                      conf: AppConf,
                      userRepo: OrgaUserRepo,
                      val groupRepo: OrgaGroupRepo,
                      cfpRepo: OrgaCfpRepo,
                      proposalRepo: OrgaProposalRepo,
                      commentRepo: OrgaCommentRepo,
                      userRequestRepo: OrgaUserRequestRepo) extends ApiCtrl(cc, silhouette, conf) with ApiCtrl.OrgaAction {
  def list(group: Group.Slug, cfp: Cfp.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiProposal.Orga]](group) { implicit req =>
    for {
      proposals <- proposalRepo.listFull(cfp, params)
      users <- userRepo.list(proposals.items.flatMap(_.users))
    } yield ApiResult.of(proposals, (p: Proposal.Full) => ApiProposal.orga(p, users))
  }

  def listAll(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiProposal.Orga]](group) { implicit req =>
    for {
      proposals <- proposalRepo.listFull(params)
      users <- userRepo.list(proposals.items.flatMap(_.users))
    } yield ApiResult.of(proposals, (p: Proposal.Full) => ApiProposal.orga(p, users))
  }

  def detail(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[ApiProposal.Orga](group) { implicit req =>
    (for {
      proposalElt <- OptionT(proposalRepo.findFull(cfp, proposal))
      users <- OptionT.liftF(userRepo.list(proposalElt.users))
      res = ApiResult.of(ApiProposal.orga(proposalElt, users))
    } yield res).value.map(_.getOrElse(proposalNotFound(cfp, proposal)))
  }

  def ratings(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Seq[ApiProposal.Orga.Rating]](group) { implicit req =>
    for {
      ratings <- proposalRepo.listRatings(proposal)
      users <- userRepo.list(ratings.flatMap(_.users))
    } yield ApiResult.of(ratings.map(ApiProposal.Orga.Rating.from(_, users)))
  }

  def speakerComments(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Seq[ApiComment]](group) { implicit req =>
    commentRepo.getComments(proposal).map(comments => ApiResult.of(comments.map(ApiComment.from)))
  }

  def orgaComments(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Seq[ApiComment]](group) { implicit req =>
    commentRepo.getOrgaComments(proposal).map(comments => ApiResult.of(comments.map(ApiComment.from)))
  }

  def invites(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Int](group) { implicit req =>
    userRequestRepo.listPendingInvites(proposal)
    ??? // TODO
  }
} 
Example 37
Source File: CredentialStore.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.credentials

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import tsec.passwordhashers._
import tsec.passwordhashers.jca._


  def updateCredentials(credentials: C, update: P => F[Unit]): F[Unit] =
    putCredentials(credentials, update)

  def isAuthenticated(credentials: C): F[Boolean]
}

abstract class PasswordStore[F[_], Id, P](implicit P: PasswordHasher[F, P], F: Sync[F])
    extends CredentialStore[F, RawCredentials[Id], PasswordHash[P]] {

  def retrievePass(id: Id): OptionT[F, PasswordHash[P]]

  def putCredentials(credentials: RawCredentials[Id], put: PasswordHash[P] => F[Unit]): F[Unit] =
    for {
      hash <- P.hashpw(credentials.rawPassword)
      _    <- put(hash)
    } yield ()

  def putCredentials(raw: Array[Byte], put: PasswordHash[P] => F[Unit]): F[Unit] =
    for {
      hash <- P.hashpw(raw)
      _    <- put(hash)
    } yield ()

  def putCredentials(raw: Array[Char], put: PasswordHash[P] => F[Unit]): F[Unit] =
    for {
      hash <- P.hashpw(raw)
      _    <- put(hash)
    } yield ()

  def isAuthenticated(credentials: RawCredentials[Id]): F[Boolean] =
    for {
      pass <- retrievePass(credentials.identity)
        .getOrElseF(F.raiseError(CredentialsError("No such user")))
      check <- P.checkpwBool(credentials.rawPassword, pass)
    } yield check

  def isAuthenticated(id: Id, raw: Array[Byte]): F[Boolean] =
    for {
      pass <- retrievePass(id)
        .getOrElseF(F.raiseError(CredentialsError("No such user")))
      check <- P.checkpwBool(raw, pass)
    } yield check

  def isAuthenticated(id: Id, raw: Array[Char]): F[Boolean] =
    for {
      pass <- retrievePass(id)
        .getOrElseF(F.raiseError(CredentialsError("No such user")))
      check <- P.checkpwBool(raw, pass)
    } yield check
}

trait SCryptPasswordStore[F[_], Id] extends PasswordStore[F, Id, SCrypt]

trait BCryptPasswordStore[F[_], Id] extends PasswordStore[F, Id, BCrypt] 
Example 38
Source File: KamonSupport.scala    From kamon-http4s   with Apache License 2.0 5 votes vote down vote up
package kamon.http4s
package middleware.server

import cats.data.{Kleisli, OptionT}
import cats.effect.{Resource, Sync}
import cats.implicits._
import kamon.Kamon
import kamon.context.Storage
import kamon.instrumentation.http.HttpServerInstrumentation.RequestHandler
import kamon.instrumentation.http.HttpServerInstrumentation
import org.http4s.{HttpRoutes, Request, Response}

object KamonSupport {

  def apply[F[_]: Sync](service: HttpRoutes[F], interface: String, port: Int): HttpRoutes[F] = {
    val httpServerConfig = Kamon.config().getConfig("kamon.instrumentation.http4s.server")
    val instrumentation = HttpServerInstrumentation.from(httpServerConfig, "http4s.server", interface, port)

    Kleisli(kamonService[F](service, instrumentation)(_))
  }


  private def kamonService[F[_]](service: HttpRoutes[F], instrumentation: HttpServerInstrumentation)
                                (request: Request[F])
                                (implicit F: Sync[F]): OptionT[F, Response[F]] = OptionT {
    getHandler(instrumentation)(request).use { handler =>
      for {
        resOrUnhandled  <- service(request).value.attempt
        respWithContext <- kamonServiceHandler(handler, resOrUnhandled, instrumentation.settings)
      } yield respWithContext
    }
  }

  private def processRequest[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, RequestHandler] =
    Resource.make(F.delay(requestHandler.requestReceived()))(h => F.delay(h.responseSent()))

  private def withContext[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, Storage.Scope] =
    Resource.make(F.delay(Kamon.storeContext(requestHandler.context)))( scope => F.delay(scope.close()))


  private def getHandler[F[_]](instrumentation: HttpServerInstrumentation)(request: Request[F])(implicit F: Sync[F]): Resource[F, RequestHandler] =
    for {
      handler <- Resource.liftF(F.delay(instrumentation.createHandler(buildRequestMessage(request))))
      _       <- processRequest(handler)
      _       <- withContext(handler)
    } yield handler

  private def kamonServiceHandler[F[_]](requestHandler: RequestHandler,
                                        e: Either[Throwable, Option[Response[F]]],
                                       settings: HttpServerInstrumentation.Settings)
                                       (implicit F: Sync[F]): F[Option[Response[F]]] =
    e match {
      case Left(e) =>
        F.delay {
          requestHandler.span.fail(e.getMessage)
          Some(requestHandler.buildResponse(errorResponseBuilder, requestHandler.context))
        } *> F.raiseError(e)
      case Right(None) =>
        F.delay {
          requestHandler.span.name(settings.unhandledOperationName)
          val response: Response[F] = requestHandler.buildResponse[Response[F]](
            notFoundResponseBuilder, requestHandler.context
          )
          Some(response)
        }
      case Right(Some(response)) =>
        F.delay {
          val a = requestHandler.buildResponse(getResponseBuilder(response), requestHandler.context)
          Some(a)
        }
    }

} 
Example 39
Source File: package.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec

import cats.data.OptionT
import org.bouncycastle.util.encoders.Hex
import org.http4s.server.Middleware
import org.http4s.{Request, Response}
import tsec.common.ManagedRandom

package object csrf {

  type CSRFToken = CSRFToken.Token

  object CSRFToken extends ManagedRandom {
    type Token <: String

    def apply(s: String): CSRFToken   = s.asInstanceOf[CSRFToken]
    def subst[F[_]](value: F[String]): F[CSRFToken] = value.asInstanceOf[F[CSRFToken]]

    def generateHexBase(tokenLength: Int = 32): String = {
      val tokenBytes = new Array[Byte](tokenLength)
      nextBytes(tokenBytes)
      Hex.toHexString(tokenBytes)
    }
  }

  type CSRFMiddleware[F[_]] =
    Middleware[OptionT[F, ?], Request[F], Response[F], Request[F], Response[F]]

} 
Example 40
Source File: BLPAuthorization.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authorization

import cats.MonadError
import cats.data.OptionT
import cats.syntax.functor._
import tsec.authentication


sealed abstract case class BLPWriteAction[F[_], Role, A, Auth](authLevel: Role)(
    implicit authInfo: AuthorizationInfo[F, Role, A],
    enum: SimpleAuthEnum[Role, Int],
    F: MonadError[F, Throwable]
) extends BLPAuthorization[F, A, Auth] {
  def isAuthorized(
      toAuth: authentication.SecuredRequest[F, A, Auth]
  ): OptionT[F, authentication.SecuredRequest[F, A, Auth]] = {
    val out = authInfo.fetchInfo(toAuth.identity).map { info =>
      val userAuthLevel = enum.getRepr(info)
      if (enum.contains(info) && userAuthLevel == enum.getRepr(authLevel))
        Some(toAuth)
      else
        None
    }
    OptionT(out)
  }
}

object BLPWriteAction {
  def apply[F[_], Role, A, Auth](authLevel: Role)(
      implicit authInfo: AuthorizationInfo[F, Role, A],
      enum: SimpleAuthEnum[Role, Int],
      F: MonadError[F, Throwable]
  ): F[BLPWriteAction[F, Role, A, Auth]] =
    if (enum.getRepr(authLevel) < 0)
      F.raiseError(InvalidAuthLevelError)
    else
      F.pure(new BLPWriteAction[F, Role, A, Auth](authLevel) {})
} 
Example 41
Source File: BasicDAC.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authorization
import cats.{Eq, MonadError}
import cats.data.OptionT
import cats.syntax.all._
import tsec.authentication.SecuredRequest


abstract class BasicDAC[F[_], G, U, Auth](implicit eq: Eq[G], F: MonadError[F, Throwable])
    extends Authorization[F, U, Auth] {
  def fetchGroup: F[AuthGroup[G]]

  def fetchOwner: F[G]

  def fetchAccess(u: SecuredRequest[F, U, Auth]): F[G]

  def isAuthorized(toAuth: SecuredRequest[F, U, Auth]): OptionT[F, SecuredRequest[F, U, Auth]] = {
    val out = for {
      owner  <- fetchOwner
      group  <- fetchGroup
      access <- fetchAccess(toAuth)
    } yield {
      if (eq.eqv(access, owner) || group.contains(access))
        Some(toAuth)
      else
        None
    }

    OptionT(out)
  }
} 
Example 42
Source File: DynamicRBAC.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authorization

import cats.MonadError
import cats.data.OptionT
import cats.syntax.all._
import tsec.authentication

case class DynamicRBAC[F[_], Role, U, Auth](dynamic: DynamicAuthGroup[F, Role])(
    implicit authInfo: AuthorizationInfo[F, Role, U],
    enum: SimpleAuthEnum[Role, String],
    F: MonadError[F, Throwable]
) extends Authorization[F, U, Auth] {
  def isAuthorized(
      toAuth: authentication.SecuredRequest[F, U, Auth]
  ): OptionT[F, authentication.SecuredRequest[F, U, Auth]] =
    OptionT(for {
      info  <- authInfo.fetchInfo(toAuth.identity)
      group <- dynamic.fetchGroupInfo
    } yield {
      if (enum.contains(info) && group.contains(info))
        Some(toAuth)
      else
        None
    })
} 
Example 43
Source File: Authorization.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authorization

import cats.Monad
import cats.data.OptionT
import cats.kernel.Monoid
import tsec.authentication.SecuredRequest

trait Authorization[F[_], Identity, Auth] {
  def isAuthorized(toAuth: SecuredRequest[F, Identity, Auth]): OptionT[F, SecuredRequest[F, Identity, Auth]]
}

object Authorization {
  implicit def authorizationMonoid[F[_]: Monad, I, Auth]: Monoid[Authorization[F, I, Auth]] =
    new Monoid[Authorization[F, I, Auth]] {
      def empty: Authorization[F, I, Auth] = new Authorization[F, I, Auth] {

        def isAuthorized(toAuth: SecuredRequest[F, I, Auth]): OptionT[F, SecuredRequest[F, I, Auth]] =
          OptionT.pure(toAuth)
      }

      def combine(x: Authorization[F, I, Auth], y: Authorization[F, I, Auth]): Authorization[F, I, Auth] =
        new Authorization[F, I, Auth] {
          def isAuthorized(toAuth: SecuredRequest[F, I, Auth]): OptionT[F, SecuredRequest[F, I, Auth]] =
            x.isAuthorized(toAuth).flatMap(y.isAuthorized)
        }
    }
} 
Example 44
Source File: BasicRBAC.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authorization

import cats.MonadError
import cats.data.OptionT
import cats.syntax.functor._
import tsec.authentication

import scala.reflect.ClassTag

sealed abstract case class BasicRBAC[F[_], R, U, Auth](authorized: AuthGroup[R])(
    implicit role: AuthorizationInfo[F, R, U],
    enum: SimpleAuthEnum[R, String],
    F: MonadError[F, Throwable]
) extends Authorization[F, U, Auth] {

  def isAuthorized(
      toAuth: authentication.SecuredRequest[F, U, Auth]
  ): OptionT[F, authentication.SecuredRequest[F, U, Auth]] =
    OptionT {
      role.fetchInfo(toAuth.identity).map { extractedRole =>
        if (enum.contains(extractedRole) && authorized.contains(extractedRole))
          Some(toAuth)
        else
          None
      }
    }
}

object BasicRBAC {
  def apply[F[_], R: ClassTag, U, Auth](roles: R*)(
      implicit enum: SimpleAuthEnum[R, String],
      role: AuthorizationInfo[F, R, U],
      F: MonadError[F, Throwable]
  ): BasicRBAC[F, R, U, Auth] =
    fromGroup[F, R, U, Auth](AuthGroup(roles: _*))

  def fromGroup[F[_], R: ClassTag, U, Auth](valueSet: AuthGroup[R])(
      implicit role: AuthorizationInfo[F, R, U],
      enum: SimpleAuthEnum[R, String],
      F: MonadError[F, Throwable]
  ): BasicRBAC[F, R, U, Auth] = new BasicRBAC[F, R, U, Auth](valueSet) {}

  def all[F[_], R: ClassTag, U, Auth](
      implicit enum: SimpleAuthEnum[R, String],
      role: AuthorizationInfo[F, R, U],
      F: MonadError[F, Throwable]
  ): BasicRBAC[F, R, U, Auth] =
    new BasicRBAC[F, R, U, Auth](enum.viewAll) {}
} 
Example 45
Source File: HierarchyAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authorization

import cats.MonadError
import cats.data.OptionT
import cats.syntax.functor._
import tsec.authentication

sealed abstract case class HierarchyAuth[F[_], R, U, Auth](authLevel: R)(
    implicit role: AuthorizationInfo[F, R, U],
    enum: SimpleAuthEnum[R, Int],
    F: MonadError[F, Throwable]
) extends Authorization[F, U, Auth] {

  def isAuthorized(
      toAuth: authentication.SecuredRequest[F, U, Auth]
  ): OptionT[F, authentication.SecuredRequest[F, U, Auth]] =
    OptionT {
      role.fetchInfo(toAuth.identity).map { authRole =>
        val intRepr = enum.getRepr(authRole)
        if (0 <= intRepr && intRepr <= enum.getRepr(authLevel) && enum.contains(authRole))
          Some(toAuth)
        else
          None
      }
    }
}

object HierarchyAuth {

  def apply[F[_], R, U, Auth](auth: R)(
      implicit role: AuthorizationInfo[F, R, U],
      e: SimpleAuthEnum[R, Int],
      F: MonadError[F, Throwable]
  ): F[HierarchyAuth[F, R, U, Auth]] =
    if (e.getRepr(auth) < 0)
      F.raiseError[HierarchyAuth[F, R, U, Auth]](InvalidAuthLevelError)
    else
      F.pure(new HierarchyAuth[F, R, U, Auth](auth) {})
} 
Example 46
Source File: RepoConfigAlg.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.repoconfig

import better.files.File
import cats.data.OptionT
import cats.implicits._
import io.chrisdavenport.log4cats.Logger
import io.circe.config.parser
import org.scalasteward.core.application.Config
import org.scalasteward.core.data.Update
import org.scalasteward.core.io.{FileAlg, WorkspaceAlg}
import org.scalasteward.core.repoconfig.RepoConfigAlg._
import org.scalasteward.core.util.MonadThrowable
import org.scalasteward.core.vcs.data.Repo

final class RepoConfigAlg[F[_]](implicit
    config: Config,
    fileAlg: FileAlg[F],
    logger: Logger[F],
    workspaceAlg: WorkspaceAlg[F],
    F: MonadThrowable[F]
) {
  def readRepoConfigOrDefault(repo: Repo): F[RepoConfig] =
    readRepoConfig(repo).flatMap { config =>
      config.map(F.pure).getOrElse(defaultRepoConfig)
    }

  
  val defaultRepoConfig: F[RepoConfig] =
    OptionT
      .fromOption[F](config.defaultRepoConfigFile)
      .flatMap(readRepoConfigFromFile)
      .getOrElse(RepoConfig.empty)

  def readRepoConfig(repo: Repo): F[Option[RepoConfig]] =
    workspaceAlg
      .repoDir(repo)
      .flatMap(dir => readRepoConfigFromFile(dir / repoConfigBasename).value)

  private def readRepoConfigFromFile(configFile: File): OptionT[F, RepoConfig] =
    OptionT(fileAlg.readFile(configFile)).map(parseRepoConfig).flatMapF {
      case Right(repoConfig) => logger.info(s"Parsed $repoConfig").as(repoConfig.some)
      case Left(errorMsg)    => logger.info(errorMsg).as(none[RepoConfig])
    }
}

object RepoConfigAlg {
  val repoConfigBasename: String = ".scala-steward.conf"

  def parseRepoConfig(input: String): Either[String, RepoConfig] =
    parser.decode[RepoConfig](input).leftMap { error =>
      s"Failed to parse $repoConfigBasename: ${error.getMessage}"
    }

  def configToIgnoreFurtherUpdates(update: Update): String =
    update match {
      case s: Update.Single =>
        s"""updates.ignore = [ { groupId = "${s.groupId}", artifactId = "${s.artifactId.name}" } ]"""
      case g: Update.Group =>
        s"""updates.ignore = [ { groupId = "${g.groupId}" } ]"""
    }
} 
Example 47
Source File: PartialStatelessJWTAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.internal

import java.time.Instant

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import io.circe.parser.decode
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import org.http4s._
import tsec.authentication._
import tsec.common._
import tsec.jws.mac._
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.jwt.{JWTClaims, JWTPrinter}
import tsec.mac.jca._

import scala.concurrent.duration._


  def discard(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    for {
      now <- F.delay(Instant.now)
      jwt <- JWTMac
        .build[F, A](
          authenticator.jwt.body
            .withExpiry(now)
            .withJwtID(SecureRandomId.Interactive.generate),
          signingKey
        )
    } yield AugmentedJWT(authenticator.id, jwt, authenticator.identity, now, authenticator.lastTouched)
} 
Example 48
Source File: StatelessJWTAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.internal

import java.time.Instant

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import org.http4s._
import tsec.authentication._
import tsec.common._
import tsec.jws.mac._
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.jwt.JWTClaims
import tsec.mac.MAC
import tsec.mac.jca._

import scala.concurrent.duration._

private[tsec] abstract class StatelessJWTAuth[F[_], V: Decoder: Encoder.AsObject, A: JWTMacAlgo](
    val expiry: FiniteDuration,
    val maxIdle: Option[FiniteDuration],
    signingKey: MacSigningKey[A]
)(implicit F: Sync[F], cv: JWSMacCV[F, A])
    extends JWTAuthenticator[F, V, V, A] {

  private[tsec] def verifyLastTouched(body: JWTMac[A], now: Instant): F[Option[Instant]]

  def parseRaw(raw: String, request: Request[F]): OptionT[F, SecuredRequest[F, V, AugmentedJWT[A, V]]] =
    OptionT(
      (for {
        now         <- F.delay(Instant.now())
        extracted   <- cv.verifyAndParse(raw, signingKey, now)
        jwtid       <- cataOption(extracted.id)
        body        <- extracted.body.asF[F, V]
        expiry      <- cataOption(extracted.body.expiration)
        lastTouched <- verifyLastTouched(extracted, now)
        augmented = AugmentedJWT(
          SecureRandomId.coerce(jwtid),
          extracted,
          body,
          expiry,
          lastTouched
        )
        refreshed <- refresh(augmented)
      } yield SecuredRequest(request, body, refreshed).some)
        .handleError(_ => None)
    )

  def create(body: V): F[AugmentedJWT[A, V]] =
    for {
      now   <- F.delay(Instant.now())
      jwtId <- SecureRandomId.Interactive.generateF[F]
      expiryTime  = now.plusSeconds(expiry.toSeconds)
      lastTouched = touch(now)
      claims = JWTClaims(
        issuedAt = touch(now),
        jwtId = Some(jwtId),
        expiration = Some(expiryTime),
        customFields = body.asJsonObject.toList
      )
      out <- JWTMac.build[F, A](claims, signingKey)
    } yield AugmentedJWT(jwtId, out, body, expiryTime, lastTouched)

  def update(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] =
    F.pure(authenticator)

  def renew(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] =
    for {
      now <- F.delay(Instant.now())
      updatedExpiry = now.plusSeconds(expiry.toSeconds)
      authBody      = authenticator.jwt.body
      lastTouched   = touch(now)
      jwt <- JWTMac.build(
        authBody.withIATOption(lastTouched).withExpiry(updatedExpiry),
        signingKey
      )
    } yield AugmentedJWT(authenticator.id, jwt, authenticator.identity, updatedExpiry, lastTouched)

  def discard(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] =
    F.pure(authenticator.copy(jwt = JWTMac.buildToken[A](JWSMacHeader[A], JWTClaims(), MAC[A](Array.empty[Byte]))))

  def afterBlock(response: Response[F], authenticator: AugmentedJWT[A, V]): OptionT[F, Response[F]] =
    OptionT.pure[F](embed(response, authenticator))
} 
Example 49
Source File: StatefulJWTAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.internal

import java.time.Instant

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import org.http4s._
import tsec.authentication._
import tsec.common._
import tsec.jws.mac._
import tsec.jwt._
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.mac.jca._

import scala.concurrent.duration.FiniteDuration


private[tsec] abstract class StatefulJWTAuth[F[_], I, V, A: JWTMacAlgo](
    val expiry: FiniteDuration,
    val maxIdle: Option[FiniteDuration],
    tokenStore: BackingStore[F, SecureRandomId, AugmentedJWT[A, I]],
    identityStore: IdentityStore[F, I, V],
    signingKey: MacSigningKey[A]
)(implicit F: Sync[F], cv: JWSMacCV[F, A])
    extends JWTAuthenticator[F, I, V, A] {

  private[tsec] def verifyAndRefresh(
      raw: String,
      retrieved: AugmentedJWT[A, I],
      now: Instant
  ): F[AugmentedJWT[A, I]]

  def parseRaw(raw: String, request: Request[F]): OptionT[F, SecuredRequest[F, V, AugmentedJWT[A, I]]] =
    OptionT(
      (for {
        now       <- F.delay(Instant.now())
        extracted <- cv.verifyAndParse(raw, signingKey, now)
        id        <- cataOption(extracted.id)
        retrieved <- tokenStore.get(SecureRandomId(id)).orAuthFailure
        refreshed <- verifyAndRefresh(raw, retrieved, now)
        identity  <- identityStore.get(retrieved.identity).orAuthFailure
      } yield SecuredRequest(request, identity, refreshed).some)
        .handleError(_ => None)
    )

  def create(body: I): F[AugmentedJWT[A, I]] =
    for {
      cookieId <- F.delay(SecureRandomId.Interactive.generate)
      now      <- F.delay(Instant.now())
      newExpiry = now.plusSeconds(expiry.toSeconds)
      claims = JWTClaims(
        issuedAt = Some(now),
        jwtId = Some(cookieId),
        expiration = Some(newExpiry)
      )
      signed  <- JWTMac.build[F, A](claims, signingKey)
      created <- tokenStore.put(AugmentedJWT(cookieId, signed, body, newExpiry, touch(now)))
    } yield created

  def renew(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    F.delay(Instant.now()).flatMap { now =>
      val updatedExpiry = now.plusSeconds(expiry.toSeconds)
      val newBody       = authenticator.jwt.body.withExpiry(updatedExpiry)
      for {
        reSigned <- JWTMac.build[F, A](newBody, signingKey)
        updated <- tokenStore
          .update(authenticator.copy(jwt = reSigned, expiry = updatedExpiry, lastTouched = touch(now)))
      } yield updated
    }

  def update(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    tokenStore.update(authenticator)

  def discard(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    tokenStore.delete(SecureRandomId.coerce(authenticator.id)).map(_ => authenticator)

  def afterBlock(response: Response[F], authenticator: AugmentedJWT[A, I]): OptionT[F, Response[F]] =
    OptionT.pure[F](response)
} 
Example 50
Source File: SecuredRequestHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication

import cats.ApplicativeError
import cats.MonadError
import cats.data.{Kleisli, OptionT}
import cats.syntax.all._
import org.http4s._
import org.log4s._
import tsec.authorization._

sealed abstract class SecuredRequestHandler[F[_], Identity, User, Auth](
    val authenticator: Authenticator[F, Identity, User, Auth]
)(implicit F: MonadError[F, Throwable], ME: MonadError[Kleisli[OptionT[F, ?], Request[F], ?], Throwable]) {

  private[this] val cachedUnauthorized: Response[F]                       = Response[F](Status.Unauthorized)
  private[this] val defaultNotAuthenticated: Request[F] => F[Response[F]] = _ => F.pure(cachedUnauthorized)

  
  private[tsec] def default[F[_], Identity, User, Auth](
      authenticator: Authenticator[F, Identity, User, Auth]
  )(implicit F: MonadError[F, Throwable]): SecuredRequestHandler[F, Identity, User, Auth] =
    new SecuredRequestHandler[F, Identity, User, Auth](authenticator) {}

} 
Example 51
Source File: VM.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.evm

import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import jbok.common.log.Logger


  def run[F[_]: Sync](context: ProgramContext[F]): F[ProgramResult[F]] = {
    val state = ProgramState[F](context)
    OptionT.fromOption[F](PrecompiledContracts.runOptionally(state.config.preCompiledContracts, context)).getOrElseF {
      run(state).map { finalState =>
        ProgramResult[F](
          finalState.returnData,
          finalState.gas,
          finalState.world,
          finalState.addressesToDelete,
          finalState.logs,
          finalState.internalTxs,
          finalState.gasRefund,
          finalState.error,
          finalState.reverted
        )
      }
    }
  }

  private def run[F[_]: Sync](state: ProgramState[F]): F[ProgramState[F]] = {
    val byte = state.program.getByte(state.pc)
    state.config.byteToOpCode.get(byte) match {
      case Some(opCode) =>
        for {
          newState <- opCode.execute(state)
          _ <- Logger[F].trace(
            s"$opCode | pc: ${newState.pc} | depth: ${newState.env.callDepth} | gas: ${newState.gas} | stack: ${newState.stack}")
          s <- if (newState.halted || newState.reverted) newState.pure[F] else run(newState)
        } yield s

      case None =>
        state.withError(InvalidOpCode(byte)).halt.pure[F]
    }
  }
} 
Example 52
Source File: TransactionService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import jbok.codec.rlp.RlpEncoded
import jbok.core.ledger.History
import jbok.core.models.{Receipt, SignedTransaction}
import jbok.core.pool.TxPool
import jbok.core.api.{BlockTag, TransactionAPI}
import scodec.bits.ByteVector

final class TransactionService[F[_]](history: History[F], txPool: TxPool[F], helper: ServiceHelper[F])(implicit F: Sync[F]) extends TransactionAPI[F] {
  override def getTx(hash: ByteVector): F[Option[SignedTransaction]] =
    (for {
      loc   <- OptionT(history.getTransactionLocation(hash))
      block <- OptionT(history.getBlockByHash(loc.blockHash))
      stx   <- OptionT.fromOption[F](block.body.transactionList.lift(loc.txIndex))
    } yield stx).value

  override def getPendingTx(hash: ByteVector): F[Option[SignedTransaction]] =
    txPool.getPendingTransactions.map(_.keys.toList.find(_.hash == hash))

  override def getReceipt(hash: ByteVector): F[Option[Receipt]] =
    (for {
      loc      <- OptionT(history.getTransactionLocation(hash))
      block    <- OptionT(history.getBlockByHash(loc.blockHash))
      _        <- OptionT.fromOption[F](block.body.transactionList.lift(loc.txIndex))
      receipts <- OptionT(history.getReceiptsByHash(loc.blockHash))
      receipt  <- OptionT.fromOption[F](receipts.lift(loc.txIndex))
    } yield receipt).value

  override def getTxByBlockHashAndIndex(hash: ByteVector, index: Int): F[Option[SignedTransaction]] =
    (for {
      block <- OptionT(history.getBlockByHash(hash))
      stx   <- OptionT.fromOption[F](block.body.transactionList.lift(index))
    } yield stx).value

  override def getTxByBlockTagAndIndex(tag: BlockTag, index: Int): F[Option[SignedTransaction]] =
    (for {
      block <- OptionT(helper.resolveBlock(tag))
      stx   <- OptionT.fromOption[F](block.body.transactionList.lift(index))
    } yield stx).value

  override def sendTx(stx: SignedTransaction): F[ByteVector] =
    txPool.addOrUpdateTransaction(stx).as(stx.hash)

  override def sendRawTx(data: RlpEncoded): F[ByteVector] =
    for {
      stx <- F.fromEither(data.decoded[SignedTransaction])
      _   <- txPool.addOrUpdateTransaction(stx)
    } yield stx.hash
} 
Example 53
Source File: StoreUpdateService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.data.OptionT
import cats.effect.{Sync, Timer}
import cats.implicits._
import fs2._
import jbok.app.service.store.{BlockStore, TransactionStore}
import jbok.common.log.Logger
import jbok.common.math.N
import jbok.core.ledger.History
import spire.compat._

import scala.concurrent.duration._

final class StoreUpdateService[F[_]](history: History[F], blockStore: BlockStore[F], txStore: TransactionStore[F])(implicit F: Sync[F], T: Timer[F]) {
  private[this] val log = Logger[F]

  def findForkPoint(start: N): F[N] =
    for {
      hash1 <- blockStore.getBlockHashByNumber(start)
      hash2 <- history.getHashByBlockNumber(start)
      number <- (hash1, hash2) match {
        case (Some(h1), Some(h2)) if h1 == h2 => F.pure(start)
        case (Some(_), Some(_))               => findForkPoint(start - 1)
        case _                                => F.raiseError(new Exception(s"fatal error"))
      }
    } yield number

  private def delRange(start: N, end: N): F[Unit] =
    List.range(start, end + 1).traverse_ { number =>
      blockStore.delByBlockNumber(number) >> txStore.delByBlockNumber(number)
    }

  private def syncRange(start: N, end: N): F[Unit] =
    List.range(start, end + 1).traverse_ { number =>
      syncBlock(number) >> syncTransactions(number)
    }

  private def syncBlock(number: N): F[Unit] =
    for {
      header <- history.getBlockHeaderByNumber(number)
      _      <- header.fold(F.unit)(header => blockStore.insert(header.number, header.hash))
    } yield ()

  private def syncTransactions(number: N): F[Unit] =
    (for {
      hash     <- OptionT(history.getHashByBlockNumber(number))
      block    <- OptionT(history.getBlockByHash(hash))
      receipts <- OptionT(history.getReceiptsByHash(hash))
      _        <- OptionT.liftF(txStore.insertBlockTransactions(block, receipts))
    } yield ()).value.void

  def sync: F[Unit] =
    for {
      currentOpt <- blockStore.getBestBlockNumber
      fork       <- currentOpt.fold(N(0).pure[F])(current => findForkPoint(current))
      best       <- history.getBestBlockNumber
      _          <- log.i(s"current: ${fork}, best: ${best}")
      _ <- if (fork == best) {
        F.unit
      } else {
        delRange(fork, best) >> syncRange(fork, best)
      }
    } yield ()

  val stream: Stream[F, Unit] =
    Stream.eval(log.i(s"starting App/StoreUpdateService")) ++
      Stream.repeatEval(sync).metered(10.seconds)
} 
Example 54
Source File: HmacAuthMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.middleware

import java.time.{Duration, Instant}

import cats.data.{Kleisli, OptionT}
import cats.effect.Sync
import jbok.network.http.server.authentication.HMAC
import org.http4s.headers.Authorization
import org.http4s.util.CaseInsensitiveString
import org.http4s.{AuthScheme, Credentials, HttpRoutes, Request, Response, Status}
import tsec.mac.jca.{HMACSHA256, MacSigningKey}

import scala.concurrent.duration.{FiniteDuration, _}

sealed abstract class HmacAuthError(val message: String) extends Exception(message)
object HmacAuthError {
  case object NoAuthHeader     extends HmacAuthError("Could not find an Authorization header")
  case object NoDatetimeHeader extends HmacAuthError("Could not find an X-Datetime header")
  case object BadMAC           extends HmacAuthError("Bad MAC")
  case object InvalidMacFormat extends HmacAuthError("The MAC is not a valid Base64 string")
  case object InvalidDatetime  extends HmacAuthError("The datetime is not a valid UTC datetime string")
  case object Timeout          extends HmacAuthError("The request time window is closed")
}

object HmacAuthMiddleware {
  val defaultDuration: FiniteDuration = 5.minutes

  private def verifyFromHeader[F[_]](
      req: Request[F],
      key: MacSigningKey[HMACSHA256],
      duration: FiniteDuration
  ): Either[HmacAuthError, Unit] =
    for {
      authHeader <- req.headers
        .get(Authorization)
        .flatMap { t =>
          t.credentials match {
            case Credentials.Token(scheme, token) if scheme == AuthScheme.Bearer =>
              Some(token)
            case _ => None
          }
        }
        .toRight(HmacAuthError.NoAuthHeader)
      datetimeHeader <- req.headers
        .get(CaseInsensitiveString("X-Datetime"))
        .toRight(HmacAuthError.NoDatetimeHeader)
      instant <- HMAC.http.verifyFromHeader(
        req.method.name,
        req.uri.renderString,
        datetimeHeader.value,
        authHeader,
        key
      )
      _ <- Either.cond(
        Instant.now().isBefore(instant.plus(Duration.ofNanos(duration.toNanos))),
        (),
        HmacAuthError.Timeout
      )
    } yield ()

  def apply[F[_]: Sync](key: MacSigningKey[HMACSHA256], duration: FiniteDuration = defaultDuration)(routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli { req: Request[F] =>
      verifyFromHeader(req, key, duration) match {
        case Left(error) => OptionT.some[F](Response[F](Status.Forbidden).withEntity(error.message))
        case Right(_)    => routes(req)
      }
    }
} 
Example 55
Source File: HttpErrorHandler.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.route

import cats.data.{Kleisli, OptionT}
import cats.effect.IO
import org.http4s.{HttpRoutes, Request, Response}

trait HttpErrorHandler {
  def handle(routes: HttpRoutes[IO]): HttpRoutes[IO]
}

object HttpErrorHandler {

  def apply(routes: HttpRoutes[IO])(handler: PartialFunction[Throwable, IO[Response[IO]]]): HttpRoutes[IO] = {
    Kleisli { req: Request[IO] =>
      OptionT {
        routes.run(req).value.handleErrorWith { e =>
          if (handler.isDefinedAt(e)) handler(e).map(Option(_))
          else IO.raiseError(e)
        }
      }
    }
  }
} 
Example 56
Source File: QMails.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.store.queries

import cats.data.OptionT

import docspell.common._
import docspell.store.impl.Column
import docspell.store.impl.Implicits._
import docspell.store.records._

import doobie._
import doobie.implicits._

object QMails {

  def delete(coll: Ident, mailId: Ident): ConnectionIO[Int] =
    (for {
      m <- OptionT(findMail(coll, mailId))
      k <- OptionT.liftF(RSentMailItem.deleteMail(mailId))
      n <- OptionT.liftF(RSentMail.delete(m._1.id))
    } yield k + n).getOrElse(0)

  def findMail(coll: Ident, mailId: Ident): ConnectionIO[Option[(RSentMail, Ident)]] = {
    val iColl = RItem.Columns.cid.prefix("i")
    val mId   = RSentMail.Columns.id.prefix("m")

    val (cols, from) = partialFind

    val cond = Seq(mId.is(mailId), iColl.is(coll))

    selectSimple(cols, from, and(cond)).query[(RSentMail, Ident)].option
  }

  def findMails(coll: Ident, itemId: Ident): ConnectionIO[Vector[(RSentMail, Ident)]] = {
    val iColl    = RItem.Columns.cid.prefix("i")
    val tItem    = RSentMailItem.Columns.itemId.prefix("t")
    val mCreated = RSentMail.Columns.created.prefix("m")

    val (cols, from) = partialFind

    val cond = Seq(tItem.is(itemId), iColl.is(coll))

    (selectSimple(cols, from, and(cond)) ++ orderBy(mCreated.f) ++ fr"DESC")
      .query[(RSentMail, Ident)]
      .to[Vector]
  }

  private def partialFind: (Seq[Column], Fragment) = {
    val iId    = RItem.Columns.id.prefix("i")
    val tItem  = RSentMailItem.Columns.itemId.prefix("t")
    val tMail  = RSentMailItem.Columns.sentMailId.prefix("t")
    val mId    = RSentMail.Columns.id.prefix("m")
    val mUser  = RSentMail.Columns.uid.prefix("m")
    val uId    = RUser.Columns.uid.prefix("u")
    val uLogin = RUser.Columns.login.prefix("u")

    val cols = RSentMail.Columns.all.map(_.prefix("m")) :+ uLogin
    val from = RSentMail.table ++ fr"m INNER JOIN" ++
      RSentMailItem.table ++ fr"t ON" ++ tMail.is(mId) ++
      fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ tItem.is(iId) ++
      fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser)

    (cols, from)
  }

} 
Example 57
Source File: OJoex.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.ops

import scala.concurrent.ExecutionContext

import cats.data.OptionT
import cats.effect._
import cats.implicits._

import docspell.common.{Ident, NodeType}
import docspell.joexapi.client.JoexClient
import docspell.store.Store
import docspell.store.records.RNode

trait OJoex[F[_]] {

  def notifyAllNodes: F[Unit]

  def cancelJob(job: Ident, worker: Ident): F[Boolean]

}

object OJoex {

  def apply[F[_]: Sync](client: JoexClient[F], store: Store[F]): Resource[F, OJoex[F]] =
    Resource.pure[F, OJoex[F]](new OJoex[F] {
      def notifyAllNodes: F[Unit] =
        for {
          nodes <- store.transact(RNode.findAll(NodeType.Joex))
          _     <- nodes.toList.traverse(n => client.notifyJoexIgnoreErrors(n.url))
        } yield ()

      def cancelJob(job: Ident, worker: Ident): F[Boolean] =
        (for {
          node   <- OptionT(store.transact(RNode.findById(worker)))
          cancel <- OptionT.liftF(client.cancelJob(node.url, job))
        } yield cancel.success).getOrElse(false)
    })

  def create[F[_]: ConcurrentEffect](
      ec: ExecutionContext,
      store: Store[F]
  ): Resource[F, OJoex[F]] =
    JoexClient.resource(ec).flatMap(client => apply(client, store))

} 
Example 58
Source File: OJob.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.ops

import cats.data.OptionT
import cats.effect._
import cats.implicits._

import docspell.backend.ops.OJob.{CollectiveQueueState, JobCancelResult}
import docspell.common.{Ident, JobState}
import docspell.store.Store
import docspell.store.queries.QJob
import docspell.store.records.{RJob, RJobLog}

trait OJob[F[_]] {

  def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState]

  def cancelJob(id: Ident, collective: Ident): F[JobCancelResult]
}

object OJob {

  sealed trait JobCancelResult
  object JobCancelResult {
    case object Removed         extends JobCancelResult
    case object CancelRequested extends JobCancelResult
    case object JobNotFound     extends JobCancelResult

    def removed: JobCancelResult         = Removed
    def cancelRequested: JobCancelResult = CancelRequested
    def jobNotFound: JobCancelResult     = JobNotFound
  }

  case class JobDetail(job: RJob, logs: Vector[RJobLog])
  case class CollectiveQueueState(jobs: Vector[JobDetail]) {
    def queued: Vector[JobDetail] =
      jobs.filter(r => JobState.queued.contains(r.job.state))
    def done: Vector[JobDetail] =
      jobs.filter(r => JobState.done.contains(r.job.state))
    def running: Vector[JobDetail] =
      jobs.filter(_.job.state == JobState.Running)
  }

  def apply[F[_]: Sync](
      store: Store[F],
      joex: OJoex[F]
  ): Resource[F, OJob[F]] =
    Resource.pure[F, OJob[F]](new OJob[F] {

      def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] =
        store
          .transact(QJob.queueStateSnapshot(collective).take(maxResults.toLong))
          .map(t => JobDetail(t._1, t._2))
          .compile
          .toVector
          .map(CollectiveQueueState)

      def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] = {
        def remove(job: RJob): F[JobCancelResult] =
          store.transact(RJob.delete(job.id)) *> JobCancelResult.removed.pure[F]

        def tryCancel(job: RJob): F[JobCancelResult] =
          job.worker match {
            case Some(worker) =>
              for {
                flag <- joex.cancelJob(job.id, worker)
                res <-
                  if (flag) JobCancelResult.cancelRequested.pure[F]
                  else remove(job)
              } yield res
            case None =>
              remove(job)
          }

        (for {
          job <- OptionT(store.transact(RJob.findByIdAndGroup(id, collective)))
          result <- OptionT.liftF(
            if (job.isInProgress) tryCancel(job)
            else remove(job)
          )
        } yield result)
          .getOrElse(JobCancelResult.jobNotFound)
      }
    })
} 
Example 59
Source File: OUserTask.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.ops

import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.Stream

import docspell.common._
import docspell.store.queue.JobQueue
import docspell.store.usertask._

import io.circe.Encoder

trait OUserTask[F[_]] {

  
  def executeNow[A](account: AccountId, task: UserTask[A])(implicit
      E: Encoder[A]
  ): F[Unit]
}

object OUserTask {

  def apply[F[_]: Effect](
      store: UserTaskStore[F],
      queue: JobQueue[F],
      joex: OJoex[F]
  ): Resource[F, OUserTask[F]] =
    Resource.pure[F, OUserTask[F]](new OUserTask[F] {

      def executeNow[A](account: AccountId, task: UserTask[A])(implicit
          E: Encoder[A]
      ): F[Unit] =
        for {
          ptask <- task.encode.toPeriodicTask(account)
          job   <- ptask.toJob
          _     <- queue.insert(job)
          _     <- joex.notifyAllNodes
        } yield ()

      def getScanMailbox(account: AccountId): Stream[F, UserTask[ScanMailboxArgs]] =
        store
          .getByName[ScanMailboxArgs](account, ScanMailboxArgs.taskName)

      def findScanMailbox(
          id: Ident,
          account: AccountId
      ): OptionT[F, UserTask[ScanMailboxArgs]] =
        OptionT(getScanMailbox(account).find(_.id == id).compile.last)

      def deleteTask(account: AccountId, id: Ident): F[Unit] =
        (for {
          _ <- store.getByIdRaw(account, id)
          _ <- OptionT.liftF(store.deleteTask(account, id))
        } yield ()).getOrElse(())

      def submitScanMailbox(
          account: AccountId,
          task: UserTask[ScanMailboxArgs]
      ): F[Unit] =
        for {
          _ <- store.updateTask[ScanMailboxArgs](account, task)
          _ <- joex.notifyAllNodes
        } yield ()

      def getNotifyDueItems(account: AccountId): Stream[F, UserTask[NotifyDueItemsArgs]] =
        store
          .getByName[NotifyDueItemsArgs](account, NotifyDueItemsArgs.taskName)

      def findNotifyDueItems(
          id: Ident,
          account: AccountId
      ): OptionT[F, UserTask[NotifyDueItemsArgs]] =
        OptionT(getNotifyDueItems(account).find(_.id == id).compile.last)

      def submitNotifyDueItems(
          account: AccountId,
          task: UserTask[NotifyDueItemsArgs]
      ): F[Unit] =
        for {
          _ <- store.updateTask[NotifyDueItemsArgs](account, task)
          _ <- joex.notifyAllNodes
        } yield ()
    })

} 
Example 60
Source File: SentMailRoutes.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.restserver.routes

import cats.data.OptionT
import cats.effect._
import cats.implicits._

import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.ops.OMail.Sent
import docspell.common._
import docspell.restapi.model._

import emil.javamail.syntax._
import org.http4s._
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl

object SentMailRoutes {

  def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
    val dsl = new Http4sDsl[F] {}
    import dsl._

    HttpRoutes.of {
      case GET -> Root / "item" / Ident(id) =>
        for {
          all  <- backend.mail.getSentMailsForItem(user.account, id)
          resp <- Ok(SentMails(all.map(convert).toList))
        } yield resp

      case GET -> Root / "mail" / Ident(mailId) =>
        (for {
          mail <- backend.mail.getSentMail(user.account, mailId)
          resp <- OptionT.liftF(Ok(convert(mail)))
        } yield resp).getOrElseF(NotFound())

      case DELETE -> Root / "mail" / Ident(mailId) =>
        for {
          n    <- backend.mail.deleteSentMail(user.account, mailId)
          resp <- Ok(BasicResult(n > 0, s"Mails deleted: $n"))
        } yield resp
    }
  }

  def convert(s: Sent): SentMail =
    SentMail(
      s.id,
      s.senderLogin,
      s.connectionName,
      s.recipients.map(_.asUnicodeString),
      s.subject,
      s.body,
      s.created
    )
} 
Example 61
Source File: FullTextIndexRoutes.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.restserver.routes

import cats.data.OptionT
import cats.effect._
import cats.implicits._

import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.common._
import docspell.restserver.Config
import docspell.restserver.conv.Conversions

import org.http4s._
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl

object FullTextIndexRoutes {

  def secured[F[_]: Effect](
      cfg: Config,
      backend: BackendApp[F],
      user: AuthToken
  ): HttpRoutes[F] =
    if (!cfg.fullTextSearch.enabled) notFound[F]
    else {
      val dsl = Http4sDsl[F]
      import dsl._

      HttpRoutes.of {
        case POST -> Root / "reIndex" =>
          for {
            res <- backend.fulltext.reindexCollective(user.account).attempt
            resp <-
              Ok(Conversions.basicResult(res, "Full-text index will be re-created."))
          } yield resp
      }
    }

  def open[F[_]: Effect](cfg: Config, backend: BackendApp[F]): HttpRoutes[F] =
    if (!cfg.fullTextSearch.enabled) notFound[F]
    else {
      val dsl = Http4sDsl[F]
      import dsl._

      HttpRoutes.of {
        case POST -> Root / "reIndexAll" / Ident(id) =>
          for {
            res <-
              if (id.nonEmpty && id == cfg.fullTextSearch.recreateKey)
                backend.fulltext.reindexAll.attempt
              else Left(new Exception("The provided key is invalid.")).pure[F]
            resp <-
              Ok(Conversions.basicResult(res, "Full-text index will be re-created."))
          } yield resp
      }
    }

  private def notFound[F[_]: Effect]: HttpRoutes[F] =
    HttpRoutes(_ => OptionT.pure(Response.notFound[F]))
} 
Example 62
Source File: Migration.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.fts

import cats.Traverse
import cats.data.{Kleisli, OptionT}
import cats.effect._
import cats.implicits._

import docspell.common._
import docspell.ftsclient._
import docspell.joex.Config
import docspell.store.records.RFtsMigration
import docspell.store.{AddResult, Store}

case class Migration[F[_]](
    version: Int,
    engine: Ident,
    description: String,
    task: FtsWork[F]
)

object Migration {

  def apply[F[_]: Effect](
      cfg: Config.FullTextSearch,
      fts: FtsClient[F],
      store: Store[F],
      logger: Logger[F]
  ): Kleisli[F, List[Migration[F]], Unit] = {
    val ctx = FtsContext(cfg, store, fts, logger)
    Kleisli(migs => Traverse[List].sequence(migs.map(applySingle[F](ctx))).map(_ => ()))
  }

  def applySingle[F[_]: Effect](ctx: FtsContext[F])(m: Migration[F]): F[Unit] = {
    val insertRecord: F[Option[RFtsMigration]] =
      for {
        rec <- RFtsMigration.create(m.version, m.engine, m.description)
        res <- ctx.store.add(
          RFtsMigration.insert(rec),
          RFtsMigration.exists(m.version, m.engine)
        )
        ret <- res match {
          case AddResult.Success         => rec.some.pure[F]
          case AddResult.EntityExists(_) => None.pure[F]
          case AddResult.Failure(ex)     => Effect[F].raiseError(ex)
        }
      } yield ret

    (for {
      _   <- OptionT.liftF(ctx.logger.info(s"Apply ${m.version}/${m.description}"))
      rec <- OptionT(insertRecord)
      res <- OptionT.liftF(m.task.run(ctx).attempt)
      ret <- OptionT.liftF(res match {
        case Right(()) => ().pure[F]
        case Left(ex) =>
          ctx.logger.error(ex)(
            s"Applying index migration ${m.version}/${m.description} failed"
          ) *>
            ctx.store.transact(RFtsMigration.deleteById(rec.id)) *> Effect[F]
            .raiseError[Unit](
              ex
            )
      })
    } yield ret).getOrElseF(
      ctx.logger.info(s"Migration ${m.version}/${m.description} already applied.")
    )
  }
} 
Example 63
Source File: DummyCpgProviderSpec.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.cpg

import java.util.UUID

import scala.concurrent.ExecutionContext
import cats.data.OptionT
import cats.effect.{ContextShift, IO}
import org.scalatest.concurrent.Eventually

import io.shiftleft.codepropertygraph.Cpg
import io.shiftleft.cpgserver.BaseSpec
import io.shiftleft.cpgserver.query.CpgOperationResult

import scala.concurrent.duration._
import scala.language.postfixOps

class DummyCpgProviderSpec extends BaseSpec with Eventually {

  private implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)

  private def withNewCpgProvider[T](f: DummyCpgProvider => T): T = {
    f(new DummyCpgProvider)
  }

  "Creating a CPG" should {
    "return a UUID referencing the eventual CPG" in withNewCpgProvider { cpgProvider =>
      noException should be thrownBy cpgProvider.createCpg(Set.empty).unsafeRunSync()
    }
  }

  "Retrieving a CPG" should {
    "return a success if the CPG was created successfully" in withNewCpgProvider { cpgProvider =>
      val cpgId = cpgProvider.createCpg(Set.empty).unsafeRunSync()

      eventually(timeout(10 seconds), interval(1 seconds)) {
        cpgProvider.retrieveCpg(cpgId).value.unsafeRunSync() shouldBe defined
      }
    }

    "return an empty OptionT if the CPG does not exist" in withNewCpgProvider { cpgProvider =>
      cpgProvider.retrieveCpg(UUID.randomUUID) shouldBe OptionT.none[IO, CpgOperationResult[Cpg]]
    }
  }
} 
Example 64
Source File: DummyBackingStore.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.authentication

import cats.data.OptionT
import cats.effect.IO
import tsec.authentication.BackingStore

import scala.collection.mutable

object DummyBackingStore {
  def apply[I, V](getId: V => I): BackingStore[IO, I, V] = new BackingStore[IO, I, V] {
    private val storageMap = mutable.HashMap.empty[I, V]

    def put(elem: V): IO[V] = {
      val map = storageMap.put(getId(elem), elem)
      if (map.isEmpty)
        IO.pure(elem)
      else
        IO.raiseError(new IllegalArgumentException)
    }

    def get(id: I): OptionT[IO, V] =
      OptionT.fromOption[IO](storageMap.get(id))

    def update(v: V): IO[V] = {
      storageMap.update(getId(v), v)
      IO.pure(v)
    }

    def delete(id: I): IO[Unit] =
      storageMap.remove(id) match {
        case Some(_) => IO.unit
        case None    => IO.raiseError(new IllegalArgumentException)
      }
  }
} 
Example 65
Source File: SwaggerRoute.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.route

import java.util.concurrent.Executors

import scala.concurrent.ExecutionContext
import cats.data.OptionT
import cats.effect.{Blocker, ContextShift, IO}
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.io._
import org.http4s.headers.Location
import org.webjars.WebJarAssetLocator
import io.shiftleft.cpgserver.route.CpgRoute.ApiError

final class SwaggerRoute {

  private val blockingEc = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor)
  private val blocker = Blocker.liftExecutionContext(blockingEc)
  private implicit val blockingCs: ContextShift[IO] = IO.contextShift(blockingEc)

  private val swaggerUiVersion = IO { new WebJarAssetLocator().getWebJars.get("swagger-ui") }
  private val swaggerUiResources = swaggerUiVersion.map { ver =>
    s"/META-INF/resources/webjars/swagger-ui/$ver"
  }
  private val swaggerUiPath = Path("swagger-ui")

  val routes: HttpRoutes[IO] = HttpRoutes.of {
    case GET -> Root / ("swagger-ui" | "docs") =>
      PermanentRedirect(Location(Uri.unsafeFromString("swagger-ui/index.html")))

    // TODO discuss with jacob: according to scalac this is unreachable... commenting for now since it probably never worked anyway
    case req @ GET -> (Root | `swaggerUiPath`) / "swagger.yaml" =>
      StaticFile
        .fromResource("/swagger.yaml", blocker, Some(req))
        .getOrElseF(InternalServerError(ApiError("Swagger documentation is missing.").asJson))

    case req @ GET -> path if path.startsWith(swaggerUiPath) => {
      val file = path.toList.tail.mkString("/", "/", "") match {
        case f if f == "/index.html" =>
          StaticFile.fromResource[IO]("/swagger-ui/index.html", blocker, Some(req))
        case f =>
          OptionT.liftF(swaggerUiResources).flatMap { resources =>
            StaticFile.fromResource[IO](resources + f, blocker, Some(req))
          }
      }
      file.getOrElseF(InternalServerError(ApiError(s"Requested file [$file] is missing.").asJson))
    }
  }
}

object SwaggerRoute {
  def apply(): SwaggerRoute =
    new SwaggerRoute
} 
Example 66
Source File: ServerAmmoniteExecutor.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.query

import cats.data.OptionT
import cats.effect.{Blocker, ContextShift, IO}

import io.shiftleft.codepropertygraph.Cpg
import io.shiftleft.console.scripting.AmmoniteExecutor

import java.util.UUID
import java.util.concurrent.{ConcurrentHashMap, Executors}

import scala.collection.concurrent.Map
import scala.concurrent.ExecutionContext
import scala.jdk.CollectionConverters._

abstract class ServerAmmoniteExecutor(implicit cs: ContextShift[IO]) extends AmmoniteExecutor {

  private val blocker: Blocker =
    Blocker.liftExecutionContext(ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2)))

  private val queryResultMap: Map[UUID, CpgOperationResult[String]] =
    new ConcurrentHashMap[UUID, CpgOperationResult[String]].asScala

  private val uuidProvider = IO { UUID.randomUUID }

  def executeQuery(cpg: Cpg, query: String): IO[UUID] = {
    for {
      resultUuid <- uuidProvider
      _ <- blocker
        .blockOn(runQuery(query, cpg))
        .runAsync {
          case Right(result) => IO(queryResultMap.put(resultUuid, CpgOperationSuccess(result.toString))).map(_ => ())
          case Left(ex)      => IO(queryResultMap.put(resultUuid, CpgOperationFailure(ex))).map(_ => ())
        }
        .toIO
    } yield resultUuid
  }

  def retrieveQueryResult(queryId: UUID): OptionT[IO, CpgOperationResult[String]] = {
    OptionT.fromOption(queryResultMap.get(queryId))
  }

  def executeQuerySync(cpg: Cpg, query: String): IO[CpgOperationResult[String]] = {
    for {
      result <- runQuery(query, cpg)
        .map(v => CpgOperationSuccess(v.toString))
        .handleErrorWith(err => IO(CpgOperationFailure(err)))
    } yield result
  }
} 
Example 67
Source File: DummyCpgProvider.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.cpg

import java.util.UUID
import java.util.concurrent.{ConcurrentHashMap, Executors}

import scala.jdk.CollectionConverters._
import scala.collection.concurrent.Map
import scala.concurrent.ExecutionContext
import cats.data.OptionT
import cats.effect.{Blocker, ContextShift, IO}
import io.shiftleft.codepropertygraph.Cpg
import io.shiftleft.codepropertygraph.generated.nodes.NewMethod
import io.shiftleft.cpgserver.query.{CpgOperationFailure, CpgOperationResult, CpgOperationSuccess}
import io.shiftleft.passes.{CpgPass, DiffGraph}
import io.shiftleft.semanticcpg.language._


class DummyCpgProvider(implicit cs: ContextShift[IO]) extends CpgProvider {

  private val blocker: Blocker =
    Blocker.liftExecutionContext(ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2)))

  private val cpgMap: Map[UUID, CpgOperationResult[Cpg]] =
    new ConcurrentHashMap[UUID, CpgOperationResult[Cpg]].asScala

  private val uuidProvider = IO(UUID.randomUUID)

  private class MyPass(cpg: Cpg) extends CpgPass(cpg) {
    override def run(): Iterator[DiffGraph] = {
      implicit val diffGraph: DiffGraph.Builder = DiffGraph.newBuilder
      NewMethod(name = "main", isExternal = false).start.store
      Iterator(diffGraph.build())
    }
  }

  override def createCpg(filenames: Set[String]): IO[UUID] = {
    val cpg = new Cpg

    for {
      cpgId <- uuidProvider
      _ <- blocker
        .blockOn(IO(new MyPass(cpg).createAndApply()))
        .runAsync {
          case Right(_) => IO(cpgMap.put(cpgId, CpgOperationSuccess(cpg))).map(_ => ())
          case Left(ex) => IO(cpgMap.put(cpgId, CpgOperationFailure(ex))).map(_ => ())
        }
        .toIO
    } yield cpgId
  }

  override def retrieveCpg(uuid: UUID): OptionT[IO, CpgOperationResult[Cpg]] = {
    OptionT.fromOption(cpgMap.get(uuid))
  }
} 
Example 68
Source File: ConsRecordToActionHeader.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.conversions

import cats.data.OptionT
import cats.implicits._
import com.evolutiongaming.catshelper.MonadThrowable
import com.evolutiongaming.kafka.journal._
import com.evolutiongaming.kafka.journal.util.CatsHelper._
import com.evolutiongaming.skafka.Header
import scodec.bits.ByteVector

trait ConsRecordToActionHeader[F[_]] {

  def apply(consRecord: ConsRecord): OptionT[F, ActionHeader]
}

object ConsRecordToActionHeader {

  implicit def apply[F[_] : MonadThrowable](implicit
    fromBytes: FromBytes[F, Option[ActionHeader]]
  ): ConsRecordToActionHeader[F] = {

    consRecord: ConsRecord => {
      def header = consRecord
        .headers
        .find { _.key === ActionHeader.key }

      def actionHeader(header: Header) = {
        val byteVector = ByteVector.view(header.value)
        fromBytes(byteVector).adaptError { case e =>
          JournalError(s"ConsRecordToActionHeader failed for $consRecord: $e", e)
        }
      }

      for {
        header       <- header.toOptionT[F]
        actionHeader <- actionHeader(header).toOptionT
      } yield actionHeader

    }
  }
} 
Example 69
Source File: ConsRecordToActionRecord.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.conversions

import java.time.Instant

import cats.data.OptionT
import cats.implicits._
import com.evolutiongaming.catshelper.MonadThrowable
import com.evolutiongaming.kafka.journal._
import com.evolutiongaming.kafka.journal.util.CatsHelper._

trait ConsRecordToActionRecord[F[_]] {

  def apply(consRecord: ConsRecord): OptionT[F, ActionRecord[Action]]
}


object ConsRecordToActionRecord {

  implicit def apply[F[_] : MonadThrowable](implicit
    consRecordToActionHeader: ConsRecordToActionHeader[F],
    headerToTuple: HeaderToTuple[F],
  ): ConsRecordToActionRecord[F] = {

    consRecord: ConsRecord => {

      def action(key: Key, timestamp: Instant, header: ActionHeader) = {

        def append(header: ActionHeader.Append) = {
          consRecord
            .value
            .traverse { value =>
              val headers = consRecord.headers
                .filter { _.key =!= ActionHeader.key }
                .traverse { header => headerToTuple(header) }

              for {
                headers <- headers
              } yield {
                val payload = value.value
                Action.append(key, timestamp, header, payload, headers.toMap)
              }
            }
        }

        header match {
          case header: ActionHeader.Append => append(header).toOptionT
          case header: ActionHeader.Mark   => Action.mark(key, timestamp, header).pure[OptionT[F, *]]
          case header: ActionHeader.Delete => Action.delete(key, timestamp, header).pure[OptionT[F, *]]
          case header: ActionHeader.Purge  => Action.purge(key, timestamp, header).pure[OptionT[F, *]]
        }
      }

      val result = for {
        id               <- consRecord.key.toOptionT[F]
        timestampAndType <- consRecord.timestampAndType.toOptionT[F]
        header           <- consRecordToActionHeader(consRecord)
        key               = Key(id = id.value, topic = consRecord.topic)
        timestamp         = timestampAndType.timestamp
        action           <- action(key, timestamp, header)
      } yield {
        val partitionOffset = PartitionOffset(consRecord)
        ActionRecord(action, partitionOffset)
      }

      result
        .value
        .adaptError { case e =>
          JournalError(s"ConsRecordToActionRecord failed for $consRecord: $e", e)
        }
        .toOptionT
    }
  }
} 
Example 70
Source File: CatsHelper.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.util

import cats.data.OptionT
import cats.effect._
import cats.implicits._
import cats.kernel.CommutativeMonoid
import cats.{Applicative, ApplicativeError, CommutativeApplicative}
import com.evolutiongaming.kafka.journal.util.Fail.implicits._


object CatsHelper {

  implicit class CommutativeApplicativeOps(val self: CommutativeApplicative.type) extends AnyVal {

    def commutativeMonoid[F[_] : CommutativeApplicative, A: CommutativeMonoid]: CommutativeMonoid[F[A]] = {
      new CommutativeMonoid[F[A]] {
        def empty = {
          Applicative[F].pure(CommutativeMonoid[A].empty)
        }

        def combine(x: F[A], y: F[A]) = {
          Applicative[F].map2(x, y)(CommutativeMonoid[A].combine)
        }
      }
    }
  }


  implicit class FOpsCatsHelper[F[_], A](val self: F[A]) extends AnyVal {

    def error[E](implicit F: ApplicativeError[F, E]): F[Option[E]] = {
      self.redeem[Option[E]](_.some, _ => none[E])
    }
  }


  implicit class FOptionOpsCatsHelper[F[_], A](val self: F[Option[A]]) extends AnyVal {
    
    def toOptionT: OptionT[F, A] = OptionT(self)
  }


  implicit class ResourceOpsCatsHelper[F[_], A](val self: Resource[F, A]) extends AnyVal {

    def start[B](use: A => F[B])(implicit F: Concurrent[F]): F[Fiber[F, B]] = {
      StartResource(self)(use)
    }
  }


  implicit class OptionOpsCatsHelper[A](val self: Option[A]) extends AnyVal {

    def getOrError[F[_]: Applicative : Fail](name: => String): F[A] = {
      self.fold {
        s"$name is not defined".fail[F, A]
      } { a =>
        a.pure[F]
      }
    }
  }
} 
Example 71
Source File: MigrationAlg.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.scalafix

import better.files.File
import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import io.circe.config.parser.decode
import org.scalasteward.core.data.{Update, Version}
import org.scalasteward.core.io.FileAlg
import org.scalasteward.core.util.{ApplicativeThrowable, MonadThrowable}

trait MigrationAlg {
  def findMigrations(update: Update): List[Migration]
}

object MigrationAlg {
  def create[F[_]](extraMigrations: Option[File])(implicit
      fileAlg: FileAlg[F],
      F: Sync[F]
  ): F[MigrationAlg] =
    loadMigrations(extraMigrations).map { migrations =>
      new MigrationAlg {
        override def findMigrations(update: Update): List[Migration] =
          findMigrationsImpl(migrations, update)
      }
    }

  def loadMigrations[F[_]](
      extraMigrations: Option[File]
  )(implicit fileAlg: FileAlg[F], F: MonadThrowable[F]): F[List[Migration]] =
    for {
      default <-
        fileAlg
          .readResource("scalafix-migrations.conf")
          .flatMap(decodeMigrations[F](_, "default"))
      maybeExtra <- OptionT(extraMigrations.flatTraverse(fileAlg.readFile))
        .semiflatMap(decodeMigrations[F](_, "extra"))
        .value
      migrations = maybeExtra match {
        case Some(extra) if extra.disableDefaults => extra.migrations
        case Some(extra)                          => default.migrations ++ extra.migrations
        case None                                 => default.migrations
      }
    } yield migrations

  private def decodeMigrations[F[_]](content: String, tpe: String)(implicit
      F: ApplicativeThrowable[F]
  ): F[ScalafixMigrations] =
    F.fromEither(decode[ScalafixMigrations](content))
      .adaptErr(new Throwable(s"Failed to load $tpe Scalafix migrations", _))

  private def findMigrationsImpl(
      givenMigrations: List[Migration],
      update: Update
  ): List[Migration] =
    givenMigrations.filter { migration =>
      update.groupId === migration.groupId &&
      migration.artifactIds.exists(re =>
        update.artifactIds.exists(artifactId => re.r.findFirstIn(artifactId.name).isDefined)
      ) &&
      Version(update.currentVersion) < migration.newVersion &&
      Version(update.newerVersions.head) >= migration.newVersion
    }
}