Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
229 commits
Select commit Hold shift + click to select a range
57e5944
Nerf the thrall migration thread; very noisy.
tonytw1 Jun 4, 2024
0f463f4
Delete SyncChecker; not needed yet
tonytw1 May 18, 2024
2516ff0
Delete Scripts; not needed yet and difficult.
tonytw1 May 18, 2024
b0c4787
Delete GoodToGoCheck; not instance specific.
tonytw1 Jun 25, 2024
d990f35
Introduce an Instance model class and a way to infer it from a request.
tonytw1 Aug 29, 2024
49065b4
!!!!!!!!!!!!!!!!! Start of multi tenant - Redefine Service.apiUrl to …
tonytw1 Nov 10, 2024
bdcfbd1
Catch all .rootUri usages in s strings in media-api. Media API will p…
tonytw1 Aug 29, 2024
1957edb
KahunaController will need to evaluate the service URLs to pass to th…
tonytw1 May 14, 2024
ff2f13b
imgproxy is vhost based.
tonytw1 Aug 29, 2024
d59d6d5
cropper and metadata-editor are vhost based.
tonytw1 Aug 29, 2024
8b5e90c
loader, projection, usages, collections and leases move to vhost urls.
tonytw1 Nov 23, 2024
97cc090
auth, kahuna and thrall move to vhost url. Stops at extends Panda. We…
tonytw1 Aug 29, 2024
83d570c
Switch service names to instance aware instance.domain vhost names.
tonytw1 May 19, 2024
041ac78
Marking TODO corsFilter and securityHeadersFilter will need to be rew…
tonytw1 May 16, 2024
596abed
Reinstate security headers using an instance specific config.
tonytw1 Aug 10, 2024
4e98ba5
Implement instance specific CORS filter.
tonytw1 Aug 12, 2024
c488d12
Reinstate CSRF filter with needed the CORS filter to single bypass on…
tonytw1 Aug 12, 2024
8c2c9fc
Initial instance awareness. Prepare upload records a request specific…
tonytw1 Mar 14, 2026
09c134b
Message instance field moves upfrom ImageUpdate to all ExternalThrall…
tonytw1 Aug 29, 2024
587b932
Media API's ElasticSearch class accepts instances inputs into current…
tonytw1 Aug 29, 2024
73e955b
Media API's Elastic responses are probably instance specific.
tonytw1 Nov 23, 2024
a40152c
Step back to service to service calls via the full public URLs so tha…
tonytw1 Sep 8, 2024
b21c23d
Introduce a typed Instance and pass it around implicitly.
tonytw1 Nov 23, 2024
038452c
Kahuna auth uri in main.view are correct.
tonytw1 Aug 30, 2024
3492a5d
Patch instance url s"" usages with toString.
tonytw1 May 19, 2024
f99d01a
URIs take a full Instance rather than Request; use more implicits so …
tonytw1 Nov 10, 2024
dedf24e
Crop paths are prefixed with instance id like originals and thumbs.
tonytw1 Aug 29, 2024
cda415f
Collections store instance aware using Dynamo composite keys on id + …
tonytw1 May 19, 2024
f1696a2
Instance aware all collections end points.
tonytw1 May 19, 2024
a90c046
ImageCollectionsStore is instance aware.
tonytw1 Feb 26, 2026
b910c64
Add Instance aware dynamnodb
tonytw1 May 19, 2024
b60bd09
Instance aware all collections can scan all by querying on the instan…
tonytw1 Feb 26, 2026
82bd1a6
Instance aware all scanForId is instances aware.
tonytw1 Feb 26, 2026
5c82a1f
batchGet query is instance aware.
tonytw1 Apr 19, 2025
20b2ed2
InstanceAwareDynamoDB getV2, booleanGetV2 and setGetV2 methods.
tonytw1 Mar 14, 2026
90d738d
InstanceAwareDynamoDB asJsObject strips instance key.
tonytw1 Mar 14, 2026
f72cf8b
InstanceAwareDynamoDB - use InstanceKey every where.
tonytw1 Mar 14, 2026
035733e
InstanceAwareDynamoDB removeKeyV2.
tonytw1 Mar 18, 2026
ff490f5
InstanceAwareDynamoDB booleanSetOrRemoveV2.
tonytw1 Mar 18, 2026
7df4116
InstanceAwareDynamoDB setAddV2setAddV2 and setDeleteV2.
tonytw1 Mar 19, 2026
c7eda50
InstanceAwareDynamoDB jsonAddV2.
tonytw1 Mar 19, 2026
fb38879
InstanceAwareDynamoDB deleteItem no instance regression.
tonytw1 Mar 20, 2026
9c617d6
InstanceAwareDynamoDB deleteItemV2 and stringSetV2.
tonytw1 Mar 19, 2026
3708d2b
InstanceAwareDynamoDB deleting v1 methods.
tonytw1 Mar 20, 2026
7c21ab1
InstanceAwareDynamoDB deleteItem unused.
tonytw1 Mar 20, 2026
6eaf818
InstanceAwareDynamoDB deleteItemV2 is instance aware.
tonytw1 Mar 20, 2026
2114a4d
InstanceAwareDynamoDB removing unused v1 methods.
tonytw1 May 23, 2025
67e9278
InstanceAwareDynamoDB removing unused v1 methods.
tonytw1 Mar 21, 2026
80d271d
InstanceAwareDynamoDB purge v1 methods.
tonytw1 Mar 28, 2026
1853dc2
InstanceAwareDynamoDB scanByIdV2
tonytw1 Mar 28, 2026
67303bb
InstanceAwareDynamoDB batchGetV2
tonytw1 Mar 28, 2026
fa560fe
Meta data edits and syndication dynamo calls move to instance aware.
tonytw1 Mar 20, 2026
29a9665
No v1 client.
tonytw1 Mar 28, 2026
ea50008
EditsController imports.
tonytw1 Mar 14, 2026
c93fd0b
Move Leases to id + instance composite key; Scanomo had to be by past…
tonytw1 Nov 23, 2024
5313c52
LeaseStoreSpec is instance aware.
tonytw1 Mar 17, 2026
79b26e2
Upload status table reads are instance aware.
tonytw1 Nov 10, 2024
c0923c6
Patch up soft deletes table best we can given that Scanmomo doesn't s…
tonytw1 Nov 10, 2024
a45ea4e
Just stick instance in soft delete images; not that public facing.
tonytw1 Nov 10, 2024
6b62237
Begin integrating the auth provider Kinde.
tonytw1 May 21, 2024
7634ce5
Logout by clearing play session.
tonytw1 May 21, 2024
116147b
Redirect out of auth.
tonytw1 May 22, 2024
3be9c7a
/logout link needs a slash in this setup.
tonytw1 May 22, 2024
9f27d7a
Use seperate cookie (not Play session) so it be copied for internal c…
tonytw1 May 22, 2024
489f7b5
Flush cookie.
tonytw1 May 22, 2024
bae7f31
Logout from Kinde on logout.
tonytw1 May 22, 2024
2350acc
Auth can run at the root of the domain to auth all instances.
tonytw1 May 22, 2024
208bda5
Default exit to /
tonytw1 May 22, 2024
0110c1a
auth cookie not been seen on subdomains without an explicit domain set!?
tonytw1 May 22, 2024
c023d89
Redirect logout back to home page.
tonytw1 May 22, 2024
4f6a059
/auth/session needs to be on the same host cause CORS.
tonytw1 May 22, 2024
eac47a6
Capture loggedin user cookie for use with onBehalfOf like Panda.
tonytw1 May 22, 2024
997442c
/auth/session needs to be on the same host cause CORS.
tonytw1 May 22, 2024
5a74ecd
Fix share URL is double //
tonytw1 May 22, 2024
c49b7ef
Sign Kinde logged in user cookies.
tonytw1 May 23, 2024
34bf20a
Capture all Kinde user details.
tonytw1 May 23, 2024
c257e04
Resolved TODO; random session backed state for Kinde oauth dance.
tonytw1 May 25, 2024
99ea41a
Auth decorates authed user cookie with their instances on oauth callb…
tonytw1 May 30, 2024
e4c8422
Auth provider decorates Principal with allowed instances. Definition …
tonytw1 Jun 1, 2024
fc4cbb4
Use domainRoot is the as the config hook to remove hard coded griddev…
tonytw1 Jun 3, 2024
2f5f386
Inline Await and timeout on Kinde request.
tonytw1 Jun 4, 2024
2b4cd77
Allow InnerServicePrincipal to access all instances.
tonytw1 Jun 6, 2024
d896e82
Setup for non instance principals instances lookup.
tonytw1 Jun 6, 2024
cff8d62
Auth calls back to landing service for users instances instead of rea…
tonytw1 Jun 6, 2024
0a3b239
Quiet logging.
tonytw1 Jun 10, 2024
9849f05
Clean up; useless future.
tonytw1 Oct 26, 2024
7fc9bb7
Introduce CreateInstanceMessage so that thrall can respond to an on d…
tonytw1 May 28, 2024
60ac1db
CreateInstance is added to ExternalThrallMessage and passes through t…
tonytw1 Nov 23, 2024
e9a5d41
Thrall create instance calls collections to create the root Home coll…
tonytw1 Aug 30, 2024
4fa34d2
Test does not been an ElasticSearch base.
tonytw1 May 29, 2024
831bb68
Logging around new instance root collection setup.
tonytw1 Jun 6, 2024
5b360b2
Marking TODO - Usages not instance aware.
tonytw1 Jun 7, 2024
b6cd9c2
Sign cropper assets URLs.
tonytw1 Jun 7, 2024
4eabee5
Delete crops is instance aware.
tonytw1 Dec 3, 2024
07240f1
Need to fws instance aware update notifications from usage API.
tonytw1 Jun 10, 2024
c2bb815
Spike; progating instance throught the RX? channels for simple Usage …
tonytw1 Jun 10, 2024
7d9f6a0
WIP pushing instance down to Usages table for instance aware queries.
tonytw1 Mar 13, 2026
5bcca75
Setting up from instance specific usage table.
tonytw1 Jun 29, 2024
c8dd26e
Prefix usage groping with instance slash to make filterable by instance.
tonytw1 Jun 29, 2024
15c13f7
UsageTable - missing instance awareness.
tonytw1 Mar 28, 2026
2632bf9
UsageRecord persists an instance field so that it can be used as the …
tonytw1 Jun 29, 2024
2a3e210
UsageTableTest instance aware tests.
tonytw1 Mar 15, 2026
ee5c352
UsageTableTest implicits.
tonytw1 Mar 21, 2026
e53ed9f
Thrall polls for list of current instances; setting up for collection…
tonytw1 Jun 15, 2024
e64eb5a
Thrall counts total ES images foreach instance.
tonytw1 Jun 15, 2024
9623409
Thrall knows image count and total file size for instances.
tonytw1 Jun 25, 2024
0e76cac
Thrall announces instance usage onto an SQS queue for instances to pi…
tonytw1 Nov 23, 2024
43e6ad0
Thralls call back to instances endpoint is conf.
tonytw1 Jun 16, 2024
0bc9541
My instances endpoint for auth moves to config.
tonytw1 Jun 21, 2024
b5d7144
Thrall can ensure index on start up to help rebuild Elastic.
tonytw1 Jun 22, 2024
258ee6d
Push instance all the way down to fileKeyFromId to catch all usages.
tonytw1 Nov 10, 2024
6c5fe36
Instance specific Reaper
tonytw1 Jun 25, 2024
552eb3e
Reaper can run without logging to bucket.
tonytw1 Jun 25, 2024
6ea6216
Clean up; logging.
tonytw1 Jun 29, 2024
e9cd68e
Clean up; extract method.
tonytw1 Jul 12, 2024
ab8d43e
Renable image-deleted messages.
tonytw1 Sep 5, 2024
675c7cb
Restore image-deleted messages and make instance specific.
tonytw1 Sep 5, 2024
e2dd237
Marking TODO; no image-deleted notification after hard reap?
tonytw1 Nov 10, 2024
5439de3
Ingest images from instance specific top level folders in the ingest …
tonytw1 Feb 28, 2026
fa178a7
fileKeyFromId uses implicit Instance for smaller diff.
tonytw1 Aug 9, 2025
7c64df8
optimisedPngKeyFromId uses implicit Instance for smaller diff.
tonytw1 Aug 9, 2025
ccb16e3
Log principal for blocked request.
tonytw1 Sep 10, 2024
26b7e78
Enable API key access to ownered instances. Request owner instances b…
tonytw1 Sep 10, 2024
3ec7713
Remove deprecated TODO
tonytw1 Nov 7, 2024
f7108c5
Clean up.
tonytw1 Nov 7, 2024
0cf6a29
Instance usage message adds softDeletedCount field.
tonytw1 Sep 23, 2024
8956f0a
Logging.
tonytw1 Sep 23, 2024
b098c98
Refactor; extract instance message sender.
tonytw1 Sep 25, 2024
d0127af
Instance usage JSON writes.
tonytw1 Sep 25, 2024
c6650dc
Setting up to send instance message after setup has completed; SQS is…
tonytw1 Oct 4, 2024
a629f90
Multiplex instance related messages.
tonytw1 Sep 25, 2024
977b0c6
Reaper query build logging.
tonytw1 Sep 26, 2024
0413994
Reaper controller logging.
tonytw1 Sep 26, 2024
042c767
KeyStore seperates API keys by instance folder.
tonytw1 Oct 25, 2024
1b182ea
CSRF filter still problematic.
tonytw1 Oct 27, 2024
b3d1f6f
Marking TODO.
tonytw1 Nov 3, 2024
7249995
Log /management/healthcheck as debug.
tonytw1 Nov 5, 2024
599f685
Private.
tonytw1 Nov 7, 2024
987c3d6
Update TODO.
tonytw1 Nov 7, 2024
27a5200
Disable Guardian email parsing of CSV file in bucket usage updates.
tonytw1 Nov 15, 2024
1f7a0c1
Refactor; QuotaStore in it's own file to make it more visible.
tonytw1 Nov 15, 2024
9034695
usage/quotas needs a / prefix.
tonytw1 Nov 17, 2024
0ddf4f9
Imports
tonytw1 Mar 20, 2026
7709e49
We only need Environment credentials; may speed up first hit.
tonytw1 Nov 21, 2024
fb53715
Kanhuna CSP origin.full and origin.images are believed to be redundan…
tonytw1 Nov 24, 2024
27aa7d8
Resolve TODO. auth and kahuna are on the same hostname as everyother …
tonytw1 Nov 24, 2024
c97b116
Thrall exposes a /config end point so that reaping and hard delete co…
tonytw1 Nov 29, 2024
83e4e1c
Hard delete can delete any image which is in the soft deleted state; …
tonytw1 Nov 29, 2024
646748e
Thrall exposes a /config end point so that reaping and hard delete co…
tonytw1 Nov 29, 2024
c83fd28
Logging.
tonytw1 Nov 29, 2024
6c069bc
Log delete folder number of files found.
tonytw1 Dec 3, 2024
d8efb85
Logging.
tonytw1 Dec 6, 2024
d63bce6
Disable set missing description to upload filename.
tonytw1 Dec 13, 2024
c0435c9
Revert "Disable set missing description to upload filename."
tonytw1 Mar 13, 2025
de6c303
Log quota store fetch.
tonytw1 Dec 14, 2024
179576f
Log quota store fetch.
tonytw1 Dec 14, 2024
c6e10db
Spike - no op quota + usage join.
tonytw1 Dec 14, 2024
43d0511
Quota count on the right.
tonytw1 Dec 14, 2024
09e16e6
Billing. Setup a new actor to capture usage events.
tonytw1 Jan 12, 2025
1f77dc3
Move usage events to common lib for reuse.
tonytw1 Jan 6, 2025
b23429f
Usage event for download original.
tonytw1 Jan 6, 2025
eac63c8
Usage event SQS sending.
tonytw1 Jan 6, 2025
faa25db
Usage event includes date.
tonytw1 Jan 7, 2025
f604563
Emit api key used event on api auth.
tonytw1 Jan 11, 2025
bf2375e
Thrall config end point exposes maybeUploadLimitInBytes for UI.
tonytw1 Jan 12, 2025
de8003e
MB = 1024 * 1024.
tonytw1 Jan 12, 2025
1bbd0cd
Remove source.secureURL from image response.
tonytw1 Jan 14, 2025
bc2300f
Set isFeedUpload field on uploaded and ingested images.
tonytw1 Jan 14, 2025
d5936a3
isFeedUpload moves to UploadInfo.
tonytw1 Jan 15, 2025
b4d78e2
Crops sign source.file to recreate secureUrl.
tonytw1 Feb 7, 2026
f63ed78
Correct image if for ingest events.
tonytw1 Jan 19, 2025
1b8a0a7
Clean up; auth instance call simplied.
tonytw1 Jan 20, 2025
041b694
Spike; machine auth has non user endpoint.
tonytw1 Jan 20, 2025
df344e8
Api keys have instances attribute; can be checked without an attached…
tonytw1 Jan 21, 2025
6d7e4bd
Log levels.
tonytw1 Feb 5, 2025
c9073f0
api key usage is recorded by key name.
tonytw1 Jan 23, 2025
30406eb
Remove persistence.identifier config option.
tonytw1 Jan 25, 2025
5025a82
Debug - log updateStatus
tonytw1 Jan 25, 2025
8c0b897
Debug - prepared to queue request fails.
tonytw1 Jan 25, 2025
a71b910
Usage event for user auth.
tonytw1 Jan 23, 2025
f1a6b2b
Status.toString does not match the persisted format of the Dynamo tab…
tonytw1 Jan 25, 2025
f80544d
Debug - problem with uploadStatusTableWithCondition
tonytw1 Jan 25, 2025
4dba4ed
Fix Prepare -> Queued conditional update by burning table and redefin…
tonytw1 Jan 25, 2025
d72a1a7
Ingest image presigned uploads need to be into the instance folders.
tonytw1 Jan 25, 2025
9aca07b
Disable publishChangedSyndicationRightsForPhotoshoot
tonytw1 Jan 29, 2025
94ef773
Usage event for prepare upload.
tonytw1 Jan 30, 2025
78c8f68
Usage events; would like to capture user or api key inline.
tonytw1 Jan 30, 2025
4cbb1d9
Usage events; would like to capture user or api key inline.
tonytw1 Jan 30, 2025
7a3a340
Usage events; api / user recorded for prepare upload.
tonytw1 Jan 30, 2025
9896473
Usage events; api / user recorded for download image.
tonytw1 Jan 30, 2025
daba6bb
Move instances to common lib.
tonytw1 Feb 1, 2025
b322c13
Move instances to common lib.
tonytw1 Feb 1, 2025
88f54dd
Thrall emits image delete events.
tonytw1 Feb 2, 2025
f4ff81d
Preview reapable query by removing the time window when selected as i…
tonytw1 Feb 3, 2025
a38002c
ReapableEligibility checks for isFeedUpload.
tonytw1 Feb 7, 2025
a1b97b8
Logging.
tonytw1 Feb 7, 2025
2dfa548
Log image upload duration.
tonytw1 Feb 28, 2026
c72aa87
ReapableEligibility checks for isFeedUpload.
tonytw1 Feb 7, 2025
39fb421
ReapableEligibility checks for isFeedUpload.
tonytw1 Feb 7, 2025
1f367ef
Log mime type.
tonytw1 Feb 15, 2025
7a19579
Feed ingest file uploader is last folder in path.
tonytw1 Feb 26, 2025
9794048
Feed ingest file uploader is folder after feeds.
tonytw1 Feb 26, 2025
6b8e45d
Feed ingest file uploader is folder after feeds.
tonytw1 Feb 26, 2025
9aea1d8
Generalise text for Chargable usage rights description.
tonytw1 Apr 12, 2025
fbb5d39
Log S3 bulk deletes.
tonytw1 Apr 13, 2025
1625d97
Log total reapable images.
tonytw1 Apr 14, 2025
2983fdb
private
tonytw1 Apr 19, 2025
ec87a58
Thrall kinesis appName is explicitly set rather than defaulting to th…
tonytw1 Apr 20, 2025
03b0a1d
Thrall kinesis appName is explicitly set rather than defaulting to th…
tonytw1 Apr 20, 2025
7d76d39
Log stream name for Kinesis put.
tonytw1 Apr 20, 2025
aad3eba
Log initial offset.
tonytw1 Apr 20, 2025
45a63c0
Mulitple KCL streams cannot share the same checkpointing table!
tonytw1 Apr 20, 2025
c04252e
Log levels; reaper bucket not configured.
tonytw1 Apr 28, 2025
8c61706
Increase polling interval for unused /notification to 10 minutes.
tonytw1 Apr 28, 2025
1a512e7
Generalise text for Chargable usage rights description.
tonytw1 Apr 12, 2025
dbc7acf
Generalise owned by. Screen grab description uses generalised text.
tonytw1 Apr 13, 2025
5512347
Generalise owned by. Remove org-owned: prefix on image thumbnail mous…
tonytw1 Apr 12, 2025
ff17f87
Generalise owned filter names.
tonytw1 Apr 12, 2025
2859c9b
Generalise owned by - is suggestion names.
tonytw1 Feb 5, 2026
1da0200
Generalise the sensitive content explainer.
tonytw1 Mar 1, 2026
71142b3
Instances is more testable if injected rather than mixed in.
tonytw1 Sep 21, 2025
7425461
Setting up for instance aware migration. Push down the hard-coded dum…
tonytw1 Aug 17, 2025
7c12f94
Migration source and status have list of instances.
tonytw1 Aug 17, 2025
b627f7f
[multi-tenant] Refactor; group getMyInstances into Instances trait.
tonytw1 Nov 2, 2025
f4b05f7
[multi-tenant] Do not quietly recover from instances call failures. N…
tonytw1 Nov 2, 2025
0b78843
[multi-tenant] Download export link is instance aware
tonytw1 Feb 8, 2026
0149b0c
SyndicationController uses getV2 instead of jsonGet.
tonytw1 Mar 20, 2026
327f4e3
Marking TODO
tonytw1 Mar 28, 2026
3d07fca
Remove TODO this is fine.
tonytw1 Mar 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions auth/app/auth/AuthConfig.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package auth

import com.gu.mediaservice.lib.config.{CommonConfig, GridConfigResources}
import com.gu.mediaservice.model.Instance

class AuthConfig(resources: GridConfigResources) extends CommonConfig(resources) {
val rootUri: String = services.authBaseUri
val mediaApiUri: String = services.apiBaseUri
val rootUri: Instance => String = services.authBaseUri
val rootInstanceUri: Instance => String = services.authBaseInstanceUri
val mediaApiUri: Instance => String = services.apiBaseUri
}
21 changes: 13 additions & 8 deletions auth/app/auth/AuthController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import com.gu.mediaservice.lib.auth.Authentication.{InnerServicePrincipal, Machi
import com.gu.mediaservice.lib.auth.Permissions.{DeleteImage, ShowPaid, UploadImages}
import com.gu.mediaservice.lib.auth.provider.AuthenticationProviders
import com.gu.mediaservice.lib.auth.{Authentication, Authorisation, Internal}
import com.gu.mediaservice.lib.config.InstanceForRequest
import com.gu.mediaservice.lib.guardian.auth.PandaAuthenticationProvider
import com.gu.mediaservice.model.Instance
import play.api.libs.json.Json
import play.api.mvc.{BaseController, ControllerComponents, Result}
import play.api.mvc.{AnyContent, BaseController, ControllerComponents, Request, Result}

import java.net.URI
import java.util.Date
Expand All @@ -19,15 +21,15 @@ class AuthController(auth: Authentication, providers: AuthenticationProviders, v
override val controllerComponents: ControllerComponents,
authorisation: Authorisation)(implicit ec: ExecutionContext)
extends BaseController
with ArgoHelpers {
with ArgoHelpers with InstanceForRequest {

val indexResponse = {
def indexResponse()(implicit instance: Instance) = {
val indexData = Map("description" -> "This is the Auth API")
val indexLinks = List(
Link("root", config.mediaApiUri),
Link("login", config.services.loginUriTemplate),
Link("ui:logout", s"${config.rootUri}/logout"),
Link("session", s"${config.rootUri}/session")
Link("root", config.mediaApiUri(instance)),
Link("login", config.services.loginUriTemplate(instance)),
Link("ui:logout", s"${config.rootUri(instance)}/logout"),
Link("session", s"${config.rootInstanceUri(instance)}/session")
)
respond(indexData, indexLinks)
}
Expand All @@ -42,7 +44,10 @@ class AuthController(auth: Authentication, providers: AuthenticationProviders, v
}
}

def index = auth { indexResponse }
def index = auth { request =>
implicit val instance: Instance = instanceOf(request)
indexResponse()
}

def session = auth { request =>
val showPaid = authorisation.hasPermissionTo(ShowPaid)(request.user)
Expand Down
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ lazy val commonLib = project("common-lib").settings(
"com.amazonaws" % "aws-java-sdk-cloudwatch" % awsSdkVersion,
"com.amazonaws" % "aws-java-sdk-cloudfront" % awsSdkVersion,
"com.amazonaws" % "aws-java-sdk-sqs" % awsSdkVersion,
"software.amazon.awssdk" % "sqs" % awsSdkV2Version,
"com.amazonaws" % "aws-java-sdk-sns" % awsSdkVersion,
"com.amazonaws" % "aws-java-sdk-sts" % awsSdkVersion,
"com.amazonaws" % "aws-java-sdk-kinesis" % awsSdkVersion,
Expand Down Expand Up @@ -168,7 +169,8 @@ lazy val thrall = playProject("thrall", 9002)
"software.amazon.awssdk" % "dynamodb" % awsSdkV2Version,
"com.gu" %% "kcl-pekko-stream" % "0.1.0",
"org.testcontainers" % "testcontainers-elasticsearch" % "2.0.2" % Test,
"com.google.protobuf" % "protobuf-java" % "3.19.6"
"com.google.protobuf" % "protobuf-java" % "3.19.6",
"software.amazon.awssdk" % "sqs" % awsSdkV2Version
),
// amazon-kinesis-client 2.6.0 brings in a critically vulnerable version of apache avro,
// but we cannot upgrade amazon-kinesis-client further without performing the v2->v3 upgrade https://docs.aws.amazon.com/streams/latest/dev/kcl-migration-from-2-3.html
Expand Down
2 changes: 1 addition & 1 deletion collections/app/CollectionsComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class CollectionsComponents(context: Context) extends GridComponents(context, ne
val notifications = new Notifications(config)

val collections = new CollectionsController(auth, config, collectionsStore, controllerComponents)
val imageCollections = new ImageCollectionsController(auth, config, notifications, imageCollectionsStore, controllerComponents)
val imageCollections = new ImageCollectionsController(auth, notifications, imageCollectionsStore, controllerComponents)


override val router = new Routes(httpErrorHandler, collections, imageCollections, management)
Expand Down
71 changes: 39 additions & 32 deletions collections/app/controllers/CollectionsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import com.gu.mediaservice.lib.argo.model.{EmbeddedEntity, Link}
import com.gu.mediaservice.lib.auth.Authentication
import com.gu.mediaservice.lib.auth.Authentication.getIdentity
import com.gu.mediaservice.lib.collections.CollectionsManager
import com.gu.mediaservice.model.{ActionData, Collection}
import com.gu.mediaservice.lib.config.InstanceForRequest
import com.gu.mediaservice.model.{ActionData, Collection, Instance}
import lib.CollectionsConfig
import model.Node
import org.joda.time.DateTime
import play.api.libs.functional.syntax._
import play.api.libs.json._
import play.api.mvc.{BaseController, ControllerComponents}
import play.api.mvc.{BaseController, ControllerComponents, Request}
import store.{CollectionsStore, CollectionsStoreError}
import com.gu.mediaservice.lib.net.{URI => UriOps}
import software.amazon.awssdk.services.dynamodb.model.AttributeValue
import com.gu.mediaservice.lib.net.{URI => UriOps}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
Expand All @@ -31,31 +33,31 @@ object AppIndex {
}

class CollectionsController(authenticated: Authentication, config: CollectionsConfig, store: CollectionsStore,
val controllerComponents: ControllerComponents) extends BaseController with ArgoHelpers {
val controllerComponents: ControllerComponents) extends BaseController with ArgoHelpers with InstanceForRequest {

import CollectionsManager.{getCssColour, isValidPathBit, pathToUri, uriToPath}
// Stupid name clash between Argo and Play
import com.gu.mediaservice.lib.argo.model.{Action => ArgoAction}

def uri(u: String) = URI.create(u)
val collectionUri = uri(s"${config.rootUri}/collections")
def collectionUri(p: List[String] = Nil) = {
private def uri(u: String) = URI.create(u)
private def collectionUri()(implicit instance: Instance) = uri(s"${config.rootUri(instance)}/collections")
private def collectionUri(p: List[String] = Nil)(implicit instance: Instance) = {
val path = if(p.nonEmpty) s"/${pathToUri(p)}" else ""
uri(s"${config.rootUri}/collections$path")
uri(s"${config.rootUri(instance)}/collections$path")
}

val appIndex = AppIndex("media-collections", "The one stop shop for collections")
val indexLinks = List(Link("collections", collectionUri.toString))
private val appIndex = AppIndex("media-collections", "The one stop shop for collections")
private def indexLinks()(implicit instance: Instance) = List(Link("collections", collectionUri().toString))

def getNodeAction(n: Node[Collection]): Option[Link] = Some(Link("collection", collectionUri(n.fullPath).toString))
def addChildAction(pathId: List[String] = Nil): Option[ArgoAction] = Some(ArgoAction("add-child", collectionUri(pathId), "POST"))
def addChildAction(n: Node[Collection]): Option[ArgoAction] = addChildAction(n.fullPath)
def removeNodeAction(n: Node[Collection]): Option[ArgoAction] = if (n.children.nonEmpty) None else Some(
private def getNodeAction(n: Node[Collection])(implicit instance: Instance): Option[Link] = Some(Link("collection", collectionUri(n.fullPath).toString))
private def addChildAction(pathId: List[String] = Nil)(implicit instance: Instance): Option[ArgoAction] = Some(ArgoAction("add-child", collectionUri(pathId), "POST"))
private def addChildAction(n: Node[Collection])(implicit instance: Instance): Option[ArgoAction] = addChildAction(n.fullPath)
private def removeNodeAction(n: Node[Collection])(implicit instance: Instance): Option[ArgoAction] = if (n.children.nonEmpty) None else Some(
ArgoAction("remove", collectionUri(n.fullPath), "DELETE")
)

def index = authenticated { req =>
respond(appIndex, links = indexLinks)
respond(appIndex, links = indexLinks()(instanceOf(req)))
}

def collectionNotFound(path: String) =
Expand All @@ -70,15 +72,16 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
def storeError(message: String) =
respondError(InternalServerError, "collection-store-error", message)

def getActions(n: Node[Collection]): List[ArgoAction] = {
def getActions(n: Node[Collection])(implicit instance: Instance): List[ArgoAction] = {
List(addChildAction(n), removeNodeAction(n)).flatten
}

def getLinks(n: Node[Collection]): List[Link] = {
private def getLinks(n: Node[Collection])(implicit instance: Instance): List[Link] = {
List(getNodeAction(n)).flatten
}

def correctedCollections = authenticated.async { req =>
implicit val instance: Instance = instanceOf(req)
store.getAll flatMap { collections =>
val tree = Node.fromList[Collection](
collections,
Expand All @@ -100,14 +103,15 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
}
}

def allCollections = store.getAll.map { collections =>
def allCollections()(implicit instance: Instance)= store.getAll.map { collections =>
Node.fromList[Collection](
collections,
(collection) => collection.path,
(collection) => collection.description)
}

def getCollection(collectionPathId: String) = authenticated.async {
def getCollection(collectionPathId: String) = authenticated.async { request =>
implicit val instance: Instance = instanceOf(request)
store.get(uriToPath(collectionPathId)).map {
case Some(collection) =>
val node = Node(collection.path.last, Nil, collection.path, collection.path, Some(collection))
Expand All @@ -120,7 +124,18 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
}

def getCollections = authenticated.async { req =>
allCollections.map { tree =>
implicit val instance: Instance = instanceOf(req)
implicit def asArgo: Writes[Node[Collection]] = (
(__ \ "basename").write[String] ~
(__ \ "children").lazyWrite[CollectionsEntity](Writes[CollectionsEntity]
// This is so we don't have to rewrite the Write[Seq[T]]
(seq => Json.toJson(seq))).contramap(collectionsEntity(_: List[Node[Collection]])) ~
(__ \ "fullPath").write[List[String]] ~
(__ \ "data").writeNullable[Collection] ~
(__ \ "cssColour").writeNullable[String]
)(node => (node.basename, node.children, node.fullPath, node.data, getCssColour(node.fullPath)))

allCollections().map { tree =>
respond(
Json.toJson(tree)(asArgo),
actions = List(addChildAction()).flatten
Expand All @@ -134,6 +149,7 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
def addChildToRoot = addChildTo(None)
def addChildToCollection(collectionPathId: String) = addChildTo(Some(collectionPathId))
def addChildTo(collectionPathId: Option[String]) = authenticated.async(parse.json) { req =>
implicit val instance: Instance = instanceOf(req)
(req.body \ "data").asOpt[String] map { child =>
if (isValidPathBit(child)) {
val path = collectionPathId.map(uriToPath).getOrElse(Nil) :+ child
Expand All @@ -153,8 +169,8 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
}

type MaybeTree = Option[Node[Collection]]
def hasChildren(path: List[String]): Future[Boolean] =
allCollections.map { tree =>
private def hasChildren(path: List[String])(implicit instance: Instance) =
allCollections().map { tree =>

// Traverse the tree using the path
val maybeTree = path
Expand All @@ -168,6 +184,7 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
}

def removeCollection(collectionPath: String) = authenticated.async { req =>
implicit val instance: Instance = instanceOf(req)
val path = CollectionsManager.uriToPath(UriOps.encodePlus(collectionPath))

hasChildren(path).flatMap { noRemove =>
Expand Down Expand Up @@ -195,18 +212,8 @@ class CollectionsController(authenticated: Authentication, config: CollectionsCo
)(node => (node.basename, node.children, node.fullPath, node.data))

type CollectionsEntity = Seq[EmbeddedEntity[Node[Collection]]]
implicit def asArgo: Writes[Node[Collection]] = (
(__ \ "basename").write[String] ~
(__ \ "children").lazyWrite[CollectionsEntity](Writes[CollectionsEntity]
// This is so we don't have to rewrite the Write[Seq[T]]
(seq => Json.toJson(seq))).contramap(collectionsEntity) ~
(__ \ "fullPath").write[List[String]] ~
(__ \ "data").writeNullable[Collection] ~
(__ \ "cssColour").writeNullable[String]
)(node => (node.basename, node.children, node.fullPath, node.data, getCssColour(node.fullPath)))


def collectionsEntity(nodes: List[Node[Collection]]): CollectionsEntity = {
private def collectionsEntity(nodes: List[Node[Collection]])(implicit instance: Instance): CollectionsEntity = {
nodes.map(n => EmbeddedEntity(collectionUri(n.fullPath), Some(n), links = getLinks(n), actions = getActions(n)))
}

Expand Down
17 changes: 10 additions & 7 deletions collections/app/controllers/ImageCollectionsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,28 @@ import com.gu.mediaservice.lib.auth.Authentication
import com.gu.mediaservice.lib.auth.Authentication.getIdentity
import com.gu.mediaservice.lib.aws.{NoItemFound, UpdateMessage}
import com.gu.mediaservice.lib.collections.CollectionsManager
import com.gu.mediaservice.lib.config.InstanceForRequest
import com.gu.mediaservice.lib.net.{URI => UriOps}
import com.gu.mediaservice.model.{ActionData, Collection}
import com.gu.mediaservice.model.{ActionData, Collection, Instance}
import com.gu.mediaservice.syntax.MessageSubjects
import lib.{CollectionsConfig, Notifications}
import lib.Notifications
import org.joda.time.DateTime
import play.api.libs.json.Json
import play.api.mvc.{BaseController, ControllerComponents}
import store.ImageCollectionsStore

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future


class ImageCollectionsController(authenticated: Authentication, config: CollectionsConfig, notifications: Notifications,
class ImageCollectionsController(authenticated: Authentication, notifications: Notifications,
imageCollectionsStore: ImageCollectionsStore,
override val controllerComponents: ControllerComponents)
extends BaseController with MessageSubjects with ArgoHelpers {
extends BaseController with MessageSubjects with ArgoHelpers with InstanceForRequest {

import CollectionsManager.onlyLatest

def getCollections(id: String) = authenticated.async { req =>
implicit val instance: Instance = instanceOf(req)
imageCollectionsStore.get(id).map { collections =>
respond(onlyLatest(collections))
} recover {
Expand All @@ -34,6 +35,7 @@ class ImageCollectionsController(authenticated: Authentication, config: Collecti
}

def addCollection(id: String) = authenticated.async(parse.json) { req =>
implicit val instance: Instance = instanceOf(req)
(req.body \ "data").asOpt[List[String]].map { path =>
val collection = Collection.build(path, ActionData(getIdentity(req.user), DateTime.now()))
imageCollectionsStore.add(id, collection)
Expand All @@ -44,6 +46,7 @@ class ImageCollectionsController(authenticated: Authentication, config: Collecti


def removeCollection(id: String, collectionString: String) = authenticated.async { req =>
implicit val instance: Instance = instanceOf(req)
val path = CollectionsManager.uriToPath(UriOps.encodePlus(collectionString))
// We do a get to be able to find the index of the current collection, then remove it.
// Given that we're using Dynamo Lists this seemed like a decent way to do it.
Expand All @@ -63,9 +66,9 @@ class ImageCollectionsController(authenticated: Authentication, config: Collecti
}
}

def publish(id: String)(collections: List[Collection]): List[Collection] = {
def publish(id: String)(collections: List[Collection])(implicit instance: Instance): List[Collection] = {
val onlyLatestCollections = onlyLatest(collections)
val updateMessage = UpdateMessage(subject = SetImageCollections, id = Some(id), collections = Some(onlyLatestCollections))
val updateMessage = UpdateMessage(subject = SetImageCollections, id = Some(id), collections = Some(onlyLatestCollections), instance = instance)
notifications.publish(updateMessage)
onlyLatestCollections
}
Expand Down
3 changes: 2 additions & 1 deletion collections/app/lib/CollectionsConfig.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package lib

import com.gu.mediaservice.lib.config.{CommonConfig, GridConfigResources}
import com.gu.mediaservice.model.Instance


class CollectionsConfig(resources: GridConfigResources) extends CommonConfig(resources) {
val collectionsTable = string("dynamo.table.collections")
val imageCollectionsTable = string("dynamo.table.imageCollections")

val rootUri = services.collectionsBaseUri
val rootUri: Instance => String = services.collectionsBaseUri
}
Loading
Loading