Skip to content

Commit

Permalink
learningpath-api: Allow feide authentication
Browse files Browse the repository at this point in the history
This patch introduces feide authentication to the learningpath-api
wherever authentication is required.
  • Loading branch information
jnatten committed Oct 28, 2024
1 parent 0e99e45 commit 2670cca
Show file tree
Hide file tree
Showing 76 changed files with 976 additions and 669 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class ComponentRegistry(properties: ArticleApiProperties)
var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer)
lazy val searchApiClient = new SearchApiClient
lazy val feideApiClient = new FeideApiClient
lazy val myndlaApiClient = new MyNDLAApiClient
lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort)
lazy val frontpageApiClient = new FrontpageApiClient

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ trait TestEnvironment
val contentValidator: ContentValidator = mock[ContentValidator]

val ndlaClient: NdlaClient = mock[NdlaClient]
val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient]
val searchConverterService: SearchConverterService = mock[SearchConverterService]
var e4sClient: NdlaE4sClient = mock[NdlaE4sClient]
val searchApiClient: SearchApiClient = mock[SearchApiClient]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ class ComponentRegistry(properties: AudioApiProperties)
lazy val audioRepository = new AudioRepository
lazy val seriesRepository = new SeriesRepository

lazy val ndlaClient = new NdlaClient
lazy val ndlaClient = new NdlaClient
lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient

lazy val readService = new ReadService
lazy val writeService = new WriteService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ trait TestEnvironment

val s3Client: NdlaS3Client = mock[NdlaS3Client]

val ndlaClient: NdlaClient = mock[NdlaClient]
val ndlaClient: NdlaClient = mock[NdlaClient]
val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient]

val readService: ReadService = mock[ReadService]
val writeService: WriteService = mock[WriteService]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
/*
* Part of NDLA myndla-api
* Part of NDLA common
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

package no.ndla.myndlaapi.model.api
package no.ndla.common.model.api.myndla

import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}
import no.ndla.myndlaapi.model.domain.ArenaGroup
import no.ndla.common.model.domain.myndla.ArenaGroup
import sttp.tapir.Schema.annotations.description

case class MyNDLAGroup(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Part of NDLA common
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

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

import com.scalatsi.TypescriptType.{TSLiteralString, TSUnion}
import com.scalatsi.{TSNamedType, TSType}
import enumeratum.*

sealed trait ArenaGroup extends EnumEntry
object ArenaGroup extends Enum[ArenaGroup] with CirceEnum[ArenaGroup] {
case object ADMIN extends ArenaGroup
override def values: IndexedSeq[ArenaGroup] = findValues

implicit val enumTsType: TSNamedType[ArenaGroup] =
TSType.alias[ArenaGroup]("ArenaGroup", TSUnion(values.map(e => TSLiteralString(e.entryName))))
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/*
* Part of NDLA myndla-api
* Part of NDLA common
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

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

import no.ndla.common.errors.InvalidStatusException
import sttp.tapir.CodecFormat.TextPlain
import sttp.tapir.{Codec, CodecFormat, DecodeResult, Schema}

Expand Down
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
@@ -0,0 +1,40 @@
/*
* 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 MyNDLAUser(
id: Long,
feideId: String,
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
) {
// Keeping FEIDE and our data in sync
def wasUpdatedLast24h: Boolean = NDLADate.now().isBefore(lastUpdated.minusSeconds(10))

def isStudent: Boolean = userRole == UserRole.STUDENT
def isTeacher: Boolean = userRole == UserRole.EMPLOYEE
def isAdmin: Boolean = arenaGroups.contains(ArenaGroup.ADMIN)
}

object MyNDLAUser {
implicit val encoder: Encoder[MyNDLAUser] = deriveEncoder
implicit val decoder: Decoder[MyNDLAUser] = deriveDecoder
}
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
@@ -1,12 +1,11 @@
/*
* Part of NDLA myndla-api
* Part of NDLA common
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

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

import io.circe.{Decoder, Encoder}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Part of NDLA backend.common.main
* Copyright (C) 2024 NDLA
*
* See LICENSE
*
*/

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

import sttp.model.headers.{AuthenticationScheme, WWWAuthenticateChallenge}
import sttp.tapir.*
import sttp.tapir.CodecFormat.TextPlain
import sttp.tapir.EndpointInput.{AuthInfo, AuthType}

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 @@ -79,9 +79,10 @@ class ComponentRegistry(properties: ConceptApiProperties)

var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer)

lazy val ndlaClient = new NdlaClient
lazy val articleApiClient = new ArticleApiClient
lazy val searchApiClient = new SearchApiClient
lazy val ndlaClient = new NdlaClient
lazy val articleApiClient = new ArticleApiClient
lazy val searchApiClient = new SearchApiClient
lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient

lazy val importService = new ImportService

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ trait TestEnvironment
val importService: ImportService = mock[ImportService]

val ndlaClient: NdlaClient = mock[NdlaClient]
val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient]
val articleApiClient: ArticleApiClient = mock[ArticleApiClient]
val searchApiClient: SearchApiClient = mock[SearchApiClient]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,12 @@ class ComponentRegistry(properties: DraftApiProperties)
lazy val contentValidator = new ContentValidator()
lazy val importValidator = new ContentValidator()

lazy val ndlaClient = new NdlaClient
lazy val searchConverterService = new SearchConverterService
lazy val readService = new ReadService
lazy val writeService = new WriteService
lazy val reindexClient = new ReindexClient
lazy val ndlaClient = new NdlaClient
lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient
lazy val searchConverterService = new SearchConverterService
lazy val readService = new ReadService
lazy val writeService = new WriteService
lazy val reindexClient = new ReindexClient

lazy val fileStorage = new FileStorageService

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ trait TestEnvironment
val s3Client: NdlaS3Client = mock[NdlaS3Client]

val ndlaClient: NdlaClient = mock[NdlaClient]
val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient]
val searchConverterService: SearchConverterService = mock[SearchConverterService]
var e4sClient: NdlaE4sClient = mock[NdlaE4sClient]
override val learningpathApiClient: LearningpathApiClient = mock[LearningpathApiClient]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class ComponentRegistry(properties: FrontpageApiProperties)
override val internController = new InternController
val healthController = new TapirHealthController

override val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient
override val ndlaClient: NdlaClient = new NdlaClient

private val swagger = new SwaggerController(
List(
subjectPageController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@ trait TestEnvironment
override val readService: ReadService = mock[ReadService]
override val writeService: WriteService = mock[WriteService]

override val ndlaClient: NdlaClient = mock[NdlaClient]
override val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient]

def services: List[TapirController] = List.empty
}
27 changes: 14 additions & 13 deletions image-api/src/main/scala/no/ndla/imageapi/ComponentRegistry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,20 @@ class ComponentRegistry(properties: ImageApiProperties)

lazy val s3Client = new NdlaS3Client(props.StorageName, props.StorageRegion)

lazy val imageIndexService = new ImageIndexService
lazy val imageSearchService = new ImageSearchService
lazy val tagIndexService = new TagIndexService
lazy val tagSearchService = new TagSearchService
lazy val imageRepository = new ImageRepository
lazy val readService = new ReadService
lazy val writeService = new WriteService
lazy val validationService = new ValidationService
lazy val imageStorage = new AmazonImageStorageService
lazy val ndlaClient = new NdlaClient
lazy val converterService = new ConverterService
var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer)
lazy val searchConverterService = new SearchConverterService
lazy val imageIndexService = new ImageIndexService
lazy val imageSearchService = new ImageSearchService
lazy val tagIndexService = new TagIndexService
lazy val tagSearchService = new TagSearchService
lazy val imageRepository = new ImageRepository
lazy val readService = new ReadService
lazy val writeService = new WriteService
lazy val validationService = new ValidationService
lazy val imageStorage = new AmazonImageStorageService
lazy val ndlaClient = new NdlaClient
lazy val converterService = new ConverterService
var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer)
lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient
lazy val searchConverterService = new SearchConverterService

lazy val imageConverter = new ImageConverter
lazy val clock = new SystemClock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ trait TestEnvironment
val imageStorage: AmazonImageStorageService = mock[AmazonImageStorageService]

val ndlaClient: NdlaClient = mock[NdlaClient]
val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient]
val rawController: RawController = mock[RawController]
val healthController: HealthController = mock[HealthController]
val internController: InternController = mock[InternController]
Expand Down
Loading

0 comments on commit 2670cca

Please sign in to comment.