Skip to content

Commit

Permalink
hei
Browse files Browse the repository at this point in the history
  • Loading branch information
jnatten committed Oct 24, 2024
1 parent a0210b3 commit 8922db0
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Part of NDLA common
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

package no.ndla.common.model.domain.myndla

import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}

case class MyNDLAGroup(
id: String,
displayName: String,
isPrimarySchool: Boolean,
parentId: Option[String]
)

object MyNDLAGroup {
implicit val encoder: Encoder[MyNDLAGroup] = deriveEncoder
implicit val decoder: Decoder[MyNDLAGroup] = deriveDecoder
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,6 @@ import io.circe.{Decoder, Encoder}
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import no.ndla.common.model.NDLADate

case class MyNDLAGroup(id: String, displayName: String, isPrimarySchool: Boolean, parentId: Option[String])
object MyNDLAGroup {
implicit val encoder: Encoder[MyNDLAGroup] = deriveEncoder
implicit val decoder: Decoder[MyNDLAGroup] = deriveDecoder
}

case class MyNDLAUserDocument(
favoriteSubjects: Seq[String],
userRole: UserRole.Value,
lastUpdated: NDLADate,
organization: String,
groups: Seq[MyNDLAGroup],
username: String,
displayName: String,
email: String,
arenaEnabled: Boolean,
arenaGroups: List[ArenaGroup],
shareName: Boolean
) {
def toFullUser(id: Long, feideId: String): MyNDLAUser = {
MyNDLAUser(
id = id,
feideId = feideId,
favoriteSubjects = favoriteSubjects,
userRole = userRole,
lastUpdated = lastUpdated,
organization = organization,
groups = groups,
username = username,
displayName = displayName,
email = email,
arenaEnabled = arenaEnabled,
shareName = shareName,
arenaGroups = arenaGroups
)
}
}

object MyNDLAUserDocument {
implicit val encoder: Encoder[MyNDLAUserDocument] = deriveEncoder
implicit val decoder: Decoder[MyNDLAUserDocument] = deriveDecoder
}

case class MyNDLAUser(
id: Long,
feideId: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Part of NDLA common
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

package no.ndla.common.model.domain.myndla

import io.circe.{Decoder, Encoder}
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import no.ndla.common.model.NDLADate

case class MyNDLAUserDocument(
favoriteSubjects: Seq[String],
userRole: UserRole.Value,
lastUpdated: NDLADate,
organization: String,
groups: Seq[MyNDLAGroup],
username: String,
displayName: String,
email: String,
arenaEnabled: Boolean,
arenaGroups: List[ArenaGroup],
shareName: Boolean
) {
def toFullUser(id: Long, feideId: String): MyNDLAUser = {
MyNDLAUser(
id = id,
feideId = feideId,
favoriteSubjects = favoriteSubjects,
userRole = userRole,
lastUpdated = lastUpdated,
organization = organization,
groups = groups,
username = username,
displayName = displayName,
email = email,
arenaEnabled = arenaEnabled,
shareName = shareName,
arenaGroups = arenaGroups
)
}
}

object MyNDLAUserDocument {
implicit val encoder: Encoder[MyNDLAUserDocument] = deriveEncoder
implicit val decoder: Decoder[MyNDLAUserDocument] = deriveDecoder
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Part of NDLA backend.common.main
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

package no.ndla.common.model.domain.myndla.auth

import no.ndla.common.model.domain.myndla.{ArenaGroup, MyNDLAUser}
import sttp.model.headers.{AuthenticationScheme, WWWAuthenticateChallenge}
import sttp.monad.MonadError
import sttp.tapir.*
import sttp.tapir.CodecFormat.TextPlain
import sttp.tapir.EndpointInput.{AuthInfo, AuthType}
import sttp.tapir.server.PartialServerEndpoint

import scala.collection.immutable.ListMap

object AuthUtility {
private val authScheme = AuthenticationScheme.Bearer.name
private def filterHeaders(headers: List[String]) = headers.filter(_.toLowerCase.startsWith(authScheme.toLowerCase))
private def stringPrefixWithSpace = Mapping.stringPrefixCaseInsensitiveForList(authScheme + " ")
val feideTokenAuthCodec: Codec[List[String], Option[String], TextPlain] = {
val codec = implicitly[Codec[List[String], Option[String], CodecFormat.TextPlain]]
Codec
.id[List[String], CodecFormat.TextPlain](codec.format, Schema.binary)
.map(filterHeaders(_))(identity)
.map(stringPrefixWithSpace)
.mapDecode(codec.decode)(codec.encode)
.schema(codec.schema)
}

def feideOauth() = {
val authType: AuthType.ScopedOAuth2 = EndpointInput.AuthType
.OAuth2(None, None, ListMap.empty, None)
.requiredScopes(Seq.empty)

EndpointInput.Auth(
input = sttp.tapir.header("FeideAuthorization")(feideTokenAuthCodec),
challenge = WWWAuthenticateChallenge.bearer,
authType = authType,
info = AuthInfo.Empty.securitySchemeName("oauth2")
)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,27 @@

package no.ndla.myndlaapi

import no.ndla.common.model.domain.myndla.auth.AuthUtility
import no.ndla.common.model.domain.myndla.{ArenaGroup, MyNDLAUser}
import no.ndla.myndlaapi.service.UserService
import no.ndla.network.model.FeideAccessToken
import no.ndla.network.tapir.{AllErrors, TapirErrorHandling}
import sttp.model.headers.{AuthenticationScheme, WWWAuthenticateChallenge}
import sttp.monad.MonadError
import sttp.tapir.*
import sttp.tapir.CodecFormat.TextPlain
import sttp.tapir.EndpointInput.{AuthInfo, AuthType}
import sttp.tapir.server.PartialServerEndpoint

import scala.collection.immutable.ListMap

trait MyNDLAAuthHelpers {
this: UserService with TapirErrorHandling =>

object MyNDLAAuthHelpers {
private val authScheme = AuthenticationScheme.Bearer.name
private def filterHeaders(headers: List[String]) = headers.filter(_.toLowerCase.startsWith(authScheme.toLowerCase))
private def stringPrefixWithSpace = Mapping.stringPrefixCaseInsensitiveForList(authScheme + " ")
val feideTokenAuthCodec: Codec[List[String], Option[FeideAccessToken], TextPlain] = {
val codec = implicitly[Codec[List[String], Option[FeideAccessToken], CodecFormat.TextPlain]]
Codec
.id[List[String], CodecFormat.TextPlain](codec.format, Schema.binary)
.map(filterHeaders(_))(identity)
.map(stringPrefixWithSpace)
.mapDecode(codec.decode)(codec.encode)
.schema(codec.schema)
}

def feideOauth() = {
val authType: AuthType.ScopedOAuth2 = EndpointInput.AuthType
.OAuth2(None, None, ListMap.empty, None)
.requiredScopes(Seq.empty)

EndpointInput.Auth(
input = sttp.tapir.header("FeideAuthorization")(feideTokenAuthCodec),
challenge = WWWAuthenticateChallenge.bearer,
authType = authType,
info = AuthInfo.Empty.securitySchemeName("oauth2")
)
}

implicit class authlessEndpointFeideExtension[A, I, E, O, R](self: Endpoint[Unit, I, AllErrors, O, R]) {
type MaybeFeideToken = Option[FeideAccessToken]
type PartialFeideEndpoint[F[_]] = PartialServerEndpoint[MaybeFeideToken, MyNDLAUser, I, AllErrors, O, R, F]
def requireMyNDLAUser[F[_]](
requireArena: Boolean = false,
requireArenaAdmin: Boolean = false
): PartialFeideEndpoint[F] = {
val newEndpoint = self.securityIn(feideOauth())
val newEndpoint = self.securityIn(AuthUtility.feideOauth())
val authFunc: Option[FeideAccessToken] => Either[AllErrors, MyNDLAUser] = { maybeToken =>
if (requireArenaAdmin) {
userService.getArenaEnabledUser(maybeToken).handleErrorsOrOk match {
Expand Down
8 changes: 8 additions & 0 deletions network/src/main/scala/no/ndla/network/NdlaClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ trait NdlaClient {
doFetch(addCorrelationId(addForwardedAuth(request, tokenUser)))
}

def fetchWithForwardedFeideAuth[A: Decoder](request: NdlaRequest, feideToken: Option[String]): Try[A] = {
doFetch(addCorrelationId(addForwardedFeideAuth(request, feideToken)))
}

def fetchRaw(request: NdlaRequest): Try[Response[String]] =
doRequest(addCorrelationId(request))

Expand Down Expand Up @@ -92,6 +96,10 @@ trait NdlaClient {
request.auth.basic(user, password)
}

private def addForwardedFeideAuth(request: NdlaRequest, feideToken: Option[String]) = {
request.header("FeideAuthorizaton", s"Bearer $feideToken")
}

private def addForwardedAuth(request: NdlaRequest, tokenUser: Option[TokenUser]) = tokenUser match {
case Some(TokenUser(_, _, _, Some(auth))) =>
request.header(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import no.ndla.common.model.api.{MyNDLABundle, SingleResourceStats}
import no.ndla.common.model.api.config.ConfigMetaRestricted
import no.ndla.common.model.domain.ResourceType
import no.ndla.common.model.domain.config.ConfigKey
import no.ndla.common.model.api.myndla as api
import no.ndla.network.NdlaClient
import no.ndla.network.model.NdlaRequest
import sttp.client3.quick.*
Expand All @@ -25,6 +26,12 @@ trait MyNDLAApiClient {

class MyNDLAApiClient {
private val statsEndpoint = s"http://${props.MyNDLAApiHost}/myndla-api/v1/stats"
private val userEndpoint = uri"http://${props.MyNDLAApiHost}/myndla-api/v1/user"

def getUserWithFeideToken(feideToken: Option[String]): Try[api.MyNDLAUser] = {
val req = quickRequest.get(userEndpoint)
ndlaClient.fetchWithForwardedFeideAuth(req, feideToken)
}

def isWriteRestricted: Try[Boolean] = {
doRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import com.typesafe.scalalogging.StrictLogging
import io.circe.{Decoder, Encoder}
import no.ndla.common.Clock
import no.ndla.common.configuration.HasBaseProps
import no.ndla.common.model.api.myndla.MyNDLAUser
import no.ndla.common.model.domain.myndla.MyNDLAUser as DomainMyNDLAUser
import no.ndla.network.tapir.auth.{Permission, TokenUser}
import sttp.client3.Identity
import sttp.model.StatusCode
Expand Down Expand Up @@ -74,6 +76,13 @@ trait TapirController extends TapirErrorHandling {
val securityLogic = (m: MonadError[F]) => (a: Option[TokenUser]) => m.unit(authFunc(a))
PartialServerEndpoint(newEndpoint, securityLogic)
}

def withOptionalMyNDLAUser[F[_]]: PartialServerEndpoint[Option[MyNDLAUser], Option[MyNDLAUser], I, X, O, R, F] = {
val newEndpoint = self.securityIn(DomainMyNDLAUser.oauth2Input(Seq.empty))
val authFunc = (tokenUser: Option[TokenUser]) => Right(tokenUser): Either[X, Option[TokenUser]]
val securityLogic = (m: MonadError[F]) => (a: Option[TokenUser]) => m.unit(authFunc(a))
PartialServerEndpoint(newEndpoint, securityLogic)
}
}
}

Expand Down
72 changes: 0 additions & 72 deletions network/src/main/scala/no/ndla/network/tapir/auth/MyNDLAAuth.scala

This file was deleted.

0 comments on commit 8922db0

Please sign in to comment.