softwaremill / softwaremill/tapir

Differentiate security errors from business errors?

Open
#2,654 1 comment 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v2
Dominant language
Scala
Stars
1.5k
Forks
468
Avg merge
5h 37m
Merged PRs (30d)
34

Description

Currently, and to my knowledge,

The error output is unique. This does not allow you to write a lightweight implementation where you have a security error type and a business error type.

Then indeed the definition of an endpoint allows to define an output error as an Either, which allows to match each case to a valid output.

endpoint
      .errorOut(
        oneOf[Either[SecurityError, BusinessError]](
          oneOfVariantFromMatchType(StatusCode.NotFound, emptyOutputAs(Right(BusinessEntityNotFound))),
          oneOfVariantFromMatchType(StatusCode.Unauthorized, emptyOutputAs(Left(Unauthorized))),
          oneOfVariantFromMatchType(StatusCode.Forbidden, emptyOutputAs(Left(Forbidden)))
        )
      )

But this approach has heavy consequences in the definition of the logic. It forces the safety logic to be composed with a function that encapsulates the standard Either in a new level of Either, and the same operation is necessary in the server logic.

   myEndpoint
     .serverSecurityLogic(SecurityPolicy.doStuff.andThen(_.map(_.left.map[Either[SecurityError, BusinessError]](Left(_))))
     .serverLogic(principal => input => myBusinessLogic(input, principal).andThen(_.map(_.left.map[Either[ErrorInfo, BusinessError]](Right(_))))

It would be nice to have an experience like this:

endpoint
      .errorOut(statusCode(StatusCode.NotFound))
      .securityErrorOut(
       oneOf[SecurityError](
          oneOfVariant(StatusCode.Unauthorized, emptyOutputAs(Unauthorized)),
          oneOfVariant(StatusCode.Forbidden, emptyOutputAs(Forbidden))
        )
     )

Which would not impact what happens in the logics:

   myEndpoint
     .serverSecurityLogic(SecurityPolicy.doStuff) // return Either[SecurityError, Principal]
     .serverLogic(principal => input => myBusinessLogic(input, principal)) // return Either[BusinessError, Output] 

Unless there is another approach?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the endpoint.errorOut and serverSecurityLogic entry points shown in the issue, then examine how security and business errors are currently combined in endpoint definitions and server logic. Define the desired separation of outputs and logic return types, and verify that both security and business error cases map to their intended HTTP responses without nested Either values.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
api, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.