@anthusai/papyrus 1.0.0-next.62 → 1.0.0-next.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/amplify/auth/prospectus-google-client.js +66 -0
- package/amplify/auth/prospectus-google-client.ts +71 -0
- package/amplify/auth/resource.js +4 -1
- package/amplify/auth/resource.ts +5 -1
- package/amplify/data/resource.js +4 -0
- package/amplify/data/resource.ts +4 -0
- package/amplify/data/schema.js +4 -0
- package/amplify/data/schema.ts +4 -0
- package/amplify/site-backend.js +8 -1
- package/amplify/site-backend.ts +9 -1
- package/app/api/auth/prospectus/callback/route.ts +9 -0
- package/app/api/auth/prospectus/selection/route.ts +9 -0
- package/app/api/auth/prospectus/session/route.ts +9 -0
- package/app/api/auth/prospectus/sign-in/route.ts +9 -0
- package/app/api/auth/prospectus/sign-out/route.ts +9 -0
- package/components/news-desk-taxonomy-client.ts +37 -1
- package/components/newsroom-board-view.tsx +7 -8
- package/components/priming-proof-page.tsx +7 -5
- package/components/topic-steering-workspace.tsx +4 -0
- package/infra/amplify-app-shell.js +20 -2
- package/infra/amplify-app-shell.ts +22 -2
- package/infra/site-config.js +1 -1
- package/infra/site-config.ts +1 -1
- package/lib/category-repository.ts +1 -0
- package/lib/content-item-lifecycle.ts +55 -0
- package/lib/newsroom-board.ts +60 -21
- package/lib/newsroom-nav.ts +2 -1
- package/lib/newsroom-organizer-nav.ts +5 -2
- package/lib/priming-proof-report.ts +1 -0
- package/lib/prospectus-publication-access.ts +53 -0
- package/lib/prospectus-session.ts +196 -0
- package/lib/prospectus-sign-in-routes.ts +284 -0
- package/lib/site-brand.ts +19 -0
- package/package.json +11 -1
- package/routes.manifest.json +56 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CfnOutput } from "aws-cdk-lib";
|
|
2
|
+
import { CfnUserPoolClient, CfnUserPoolIdentityProvider } from "aws-cdk-lib/aws-cognito";
|
|
3
|
+
/**
|
|
4
|
+
* The Google-only app client a Papyrus site registers with Prospectus
|
|
5
|
+
* (Prospectus docs/accounts/GOOGLE_IDENTITY.md, policy google-only-unlinked-v1).
|
|
6
|
+
* It lives on the site's existing user pool next to the email + Google web
|
|
7
|
+
* client, but admits only the built-in Google provider through code + PKCE from
|
|
8
|
+
* the site server, with every native, SRP, custom and client-credentials path
|
|
9
|
+
* left off. Sessions last one access token: the server keeps no refresh token.
|
|
10
|
+
*/
|
|
11
|
+
export const PROSPECTUS_SIGN_IN_CALLBACK_PATH = "/api/auth/prospectus/callback";
|
|
12
|
+
export const PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES = 60;
|
|
13
|
+
export function prospectusSignInCallbackUrls(redirectUrls) {
|
|
14
|
+
const origins = [];
|
|
15
|
+
for (const redirectUrl of redirectUrls) {
|
|
16
|
+
try {
|
|
17
|
+
const origin = new URL(redirectUrl).origin;
|
|
18
|
+
if (origin !== "null" && !origins.includes(origin))
|
|
19
|
+
origins.push(origin);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return origins.map((origin) => `${origin}${PROSPECTUS_SIGN_IN_CALLBACK_PATH}`);
|
|
26
|
+
}
|
|
27
|
+
export function prospectusSignInLogoutUrls(redirectUrls) {
|
|
28
|
+
return prospectusSignInCallbackUrls(redirectUrls).map((callbackUrl) => `${new URL(callbackUrl).origin}/`);
|
|
29
|
+
}
|
|
30
|
+
export function addProspectusGoogleOnlyClient(authStack, userPool, redirectUrls) {
|
|
31
|
+
const callbackUrls = prospectusSignInCallbackUrls(redirectUrls);
|
|
32
|
+
if (callbackUrls.length === 0)
|
|
33
|
+
throw new Error("The Prospectus Google-only client needs at least one site origin.");
|
|
34
|
+
const googleOnlyClient = new CfnUserPoolClient(authStack, "ProspectusGoogleOnlyClient", {
|
|
35
|
+
userPoolId: userPool.userPoolId,
|
|
36
|
+
clientName: "prospectus-google-only",
|
|
37
|
+
generateSecret: false,
|
|
38
|
+
supportedIdentityProviders: ["Google"],
|
|
39
|
+
allowedOAuthFlowsUserPoolClient: true,
|
|
40
|
+
allowedOAuthFlows: ["code"],
|
|
41
|
+
allowedOAuthScopes: ["openid", "email", "profile"],
|
|
42
|
+
callbackUrLs: callbackUrls,
|
|
43
|
+
logoutUrLs: prospectusSignInLogoutUrls(redirectUrls),
|
|
44
|
+
explicitAuthFlows: ["ALLOW_REFRESH_TOKEN_AUTH"],
|
|
45
|
+
enableTokenRevocation: true,
|
|
46
|
+
preventUserExistenceErrors: "ENABLED",
|
|
47
|
+
accessTokenValidity: PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES,
|
|
48
|
+
idTokenValidity: PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES,
|
|
49
|
+
refreshTokenValidity: PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES,
|
|
50
|
+
tokenValidityUnits: { accessToken: "minutes", idToken: "minutes", refreshToken: "minutes" },
|
|
51
|
+
});
|
|
52
|
+
const googleIdentityProviders = authStack.node
|
|
53
|
+
.findAll()
|
|
54
|
+
.filter((construct) => construct instanceof CfnUserPoolIdentityProvider && construct.providerName === "Google");
|
|
55
|
+
for (const googleIdentityProvider of googleIdentityProviders)
|
|
56
|
+
googleOnlyClient.addDependency(googleIdentityProvider);
|
|
57
|
+
new CfnOutput(authStack, "ProspectusGoogleOnlyClientId", {
|
|
58
|
+
value: googleOnlyClient.ref,
|
|
59
|
+
description: "clientId for this site's Prospectus registration (accounts/papyrus-sites.json)",
|
|
60
|
+
});
|
|
61
|
+
new CfnOutput(authStack, "ProspectusGoogleOnlyIssuer", {
|
|
62
|
+
value: `https://cognito-idp.${authStack.region}.amazonaws.com/${userPool.userPoolId}`,
|
|
63
|
+
description: "issuer for this site's Prospectus registration (accounts/papyrus-sites.json)",
|
|
64
|
+
});
|
|
65
|
+
return googleOnlyClient;
|
|
66
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CfnOutput, type Stack } from "aws-cdk-lib";
|
|
2
|
+
import { CfnUserPoolClient, CfnUserPoolIdentityProvider, type IUserPool } from "aws-cdk-lib/aws-cognito";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The Google-only app client a Papyrus site registers with Prospectus
|
|
6
|
+
* (Prospectus docs/accounts/GOOGLE_IDENTITY.md, policy google-only-unlinked-v1).
|
|
7
|
+
* It lives on the site's existing user pool next to the email + Google web
|
|
8
|
+
* client, but admits only the built-in Google provider through code + PKCE from
|
|
9
|
+
* the site server, with every native, SRP, custom and client-credentials path
|
|
10
|
+
* left off. Sessions last one access token: the server keeps no refresh token.
|
|
11
|
+
*/
|
|
12
|
+
export const PROSPECTUS_SIGN_IN_CALLBACK_PATH = "/api/auth/prospectus/callback";
|
|
13
|
+
export const PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES = 60;
|
|
14
|
+
|
|
15
|
+
export function prospectusSignInCallbackUrls(redirectUrls: readonly string[]): string[] {
|
|
16
|
+
const origins: string[] = [];
|
|
17
|
+
for (const redirectUrl of redirectUrls) {
|
|
18
|
+
try {
|
|
19
|
+
const origin = new URL(redirectUrl).origin;
|
|
20
|
+
if (origin !== "null" && !origins.includes(origin)) origins.push(origin);
|
|
21
|
+
} catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return origins.map((origin) => `${origin}${PROSPECTUS_SIGN_IN_CALLBACK_PATH}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function prospectusSignInLogoutUrls(redirectUrls: readonly string[]): string[] {
|
|
29
|
+
return prospectusSignInCallbackUrls(redirectUrls).map((callbackUrl) => `${new URL(callbackUrl).origin}/`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addProspectusGoogleOnlyClient(
|
|
33
|
+
authStack: Stack,
|
|
34
|
+
userPool: IUserPool,
|
|
35
|
+
redirectUrls: readonly string[],
|
|
36
|
+
): CfnUserPoolClient {
|
|
37
|
+
const callbackUrls = prospectusSignInCallbackUrls(redirectUrls);
|
|
38
|
+
if (callbackUrls.length === 0) throw new Error("The Prospectus Google-only client needs at least one site origin.");
|
|
39
|
+
const googleOnlyClient = new CfnUserPoolClient(authStack, "ProspectusGoogleOnlyClient", {
|
|
40
|
+
userPoolId: userPool.userPoolId,
|
|
41
|
+
clientName: "prospectus-google-only",
|
|
42
|
+
generateSecret: false,
|
|
43
|
+
supportedIdentityProviders: ["Google"],
|
|
44
|
+
allowedOAuthFlowsUserPoolClient: true,
|
|
45
|
+
allowedOAuthFlows: ["code"],
|
|
46
|
+
allowedOAuthScopes: ["openid", "email", "profile"],
|
|
47
|
+
callbackUrLs: callbackUrls,
|
|
48
|
+
logoutUrLs: prospectusSignInLogoutUrls(redirectUrls),
|
|
49
|
+
explicitAuthFlows: ["ALLOW_REFRESH_TOKEN_AUTH"],
|
|
50
|
+
enableTokenRevocation: true,
|
|
51
|
+
preventUserExistenceErrors: "ENABLED",
|
|
52
|
+
accessTokenValidity: PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES,
|
|
53
|
+
idTokenValidity: PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES,
|
|
54
|
+
refreshTokenValidity: PROSPECTUS_SIGN_IN_TOKEN_VALIDITY_MINUTES,
|
|
55
|
+
tokenValidityUnits: { accessToken: "minutes", idToken: "minutes", refreshToken: "minutes" },
|
|
56
|
+
});
|
|
57
|
+
const googleIdentityProviders = authStack.node
|
|
58
|
+
.findAll()
|
|
59
|
+
.filter((construct): construct is CfnUserPoolIdentityProvider => construct instanceof CfnUserPoolIdentityProvider && construct.providerName === "Google");
|
|
60
|
+
for (const googleIdentityProvider of googleIdentityProviders) googleOnlyClient.addDependency(googleIdentityProvider);
|
|
61
|
+
|
|
62
|
+
new CfnOutput(authStack, "ProspectusGoogleOnlyClientId", {
|
|
63
|
+
value: googleOnlyClient.ref,
|
|
64
|
+
description: "clientId for this site's Prospectus registration (accounts/papyrus-sites.json)",
|
|
65
|
+
});
|
|
66
|
+
new CfnOutput(authStack, "ProspectusGoogleOnlyIssuer", {
|
|
67
|
+
value: `https://cognito-idp.${authStack.region}.amazonaws.com/${userPool.userPoolId}`,
|
|
68
|
+
description: "issuer for this site's Prospectus registration (accounts/papyrus-sites.json)",
|
|
69
|
+
});
|
|
70
|
+
return googleOnlyClient;
|
|
71
|
+
}
|
package/amplify/auth/resource.js
CHANGED
|
@@ -32,8 +32,11 @@ export function authConfigFromEnv() {
|
|
|
32
32
|
applyCognitoDomainPrefix: process.env.PAPYRUS_APPLY_COGNITO_DOMAIN_PREFIX === "true",
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
export function siteAuthRedirectUrls(config) {
|
|
36
|
+
return config.redirectUrls ?? DEFAULT_AUTH_REDIRECT_URLS;
|
|
37
|
+
}
|
|
35
38
|
export function defineSiteAuth(config) {
|
|
36
|
-
const authRedirectUrls = expandNewsroomRedirectUrls(config
|
|
39
|
+
const authRedirectUrls = expandNewsroomRedirectUrls(siteAuthRedirectUrls(config), config.newsroomBasePath);
|
|
37
40
|
const disableGoogleOAuth = config.disableGoogleOAuth === true;
|
|
38
41
|
return defineAuth({
|
|
39
42
|
loginWith: {
|
package/amplify/auth/resource.ts
CHANGED
|
@@ -37,9 +37,13 @@ export function authConfigFromEnv(): PapyrusAuthConfig {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
export function siteAuthRedirectUrls(config: PapyrusAuthConfig): string[] {
|
|
41
|
+
return config.redirectUrls ?? DEFAULT_AUTH_REDIRECT_URLS;
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
export function defineSiteAuth(config: PapyrusAuthConfig) {
|
|
41
45
|
const authRedirectUrls = expandNewsroomRedirectUrls(
|
|
42
|
-
config
|
|
46
|
+
siteAuthRedirectUrls(config),
|
|
43
47
|
config.newsroomBasePath,
|
|
44
48
|
);
|
|
45
49
|
const disableGoogleOAuth = config.disableGoogleOAuth === true;
|
package/amplify/data/resource.js
CHANGED
|
@@ -893,9 +893,11 @@ const schema = a.schema({
|
|
|
893
893
|
createdAt: a.datetime().required(),
|
|
894
894
|
updatedAt: a.datetime().required(),
|
|
895
895
|
newsroomFeedKey: a.string(),
|
|
896
|
+
itemLineageId: a.id(),
|
|
896
897
|
})
|
|
897
898
|
.secondaryIndexes((index) => [
|
|
898
899
|
index("newsroomFeedKey").sortKeys(["createdAt"]).queryField("listAssignmentsByNewsroomFeedAndCreatedAt"),
|
|
900
|
+
index("itemLineageId").sortKeys(["createdAt"]).queryField("listAssignmentsByItemLineageAndCreatedAt"),
|
|
899
901
|
index("queueStatusKey").sortKeys(["priority", "createdAt"]).queryField("listAssignmentsByQueueStatusAndPriority"),
|
|
900
902
|
index("assignmentTypeKey").sortKeys(["status", "createdAt"]).queryField("listAssignmentsByTypeStatusAndCreatedAt"),
|
|
901
903
|
index("status").sortKeys(["updatedAt"]).queryField("listAssignmentsByStatusAndUpdatedAt"),
|
|
@@ -1632,6 +1634,7 @@ const schema = a.schema({
|
|
|
1632
1634
|
type: a.string().required(),
|
|
1633
1635
|
status: a.string().required(),
|
|
1634
1636
|
typeStatus: a.string().required(),
|
|
1637
|
+
stage: a.string(),
|
|
1635
1638
|
slug: a.string().required(),
|
|
1636
1639
|
shortSlug: a.string(),
|
|
1637
1640
|
section: a.string(),
|
|
@@ -1663,6 +1666,7 @@ const schema = a.schema({
|
|
|
1663
1666
|
index("typeStatus").sortKeys(["publishedAt"]).queryField("listItemsByTypeStatusAndPublishedAt"),
|
|
1664
1667
|
index("sectionStatus").sortKeys(["publishedAt"]).queryField("listItemsBySectionStatusAndPublishedAt"),
|
|
1665
1668
|
index("versionState").sortKeys(["updatedAt"]).queryField("listItemsByVersionStateAndUpdatedAt"),
|
|
1669
|
+
index("stage").sortKeys(["updatedAt"]).queryField("listItemsByStageAndUpdatedAt"),
|
|
1666
1670
|
])
|
|
1667
1671
|
.authorization((allow) => [
|
|
1668
1672
|
allow.groups(contentWriteGroups),
|
package/amplify/data/resource.ts
CHANGED
|
@@ -970,9 +970,11 @@ const schema = a.schema({
|
|
|
970
970
|
createdAt: a.datetime().required(),
|
|
971
971
|
updatedAt: a.datetime().required(),
|
|
972
972
|
newsroomFeedKey: a.string(),
|
|
973
|
+
itemLineageId: a.id(),
|
|
973
974
|
})
|
|
974
975
|
.secondaryIndexes((index) => [
|
|
975
976
|
index("newsroomFeedKey").sortKeys(["createdAt"]).queryField("listAssignmentsByNewsroomFeedAndCreatedAt"),
|
|
977
|
+
index("itemLineageId").sortKeys(["createdAt"]).queryField("listAssignmentsByItemLineageAndCreatedAt"),
|
|
976
978
|
index("queueStatusKey").sortKeys(["priority", "createdAt"]).queryField("listAssignmentsByQueueStatusAndPriority"),
|
|
977
979
|
index("assignmentTypeKey").sortKeys(["status", "createdAt"]).queryField("listAssignmentsByTypeStatusAndCreatedAt"),
|
|
978
980
|
index("status").sortKeys(["updatedAt"]).queryField("listAssignmentsByStatusAndUpdatedAt"),
|
|
@@ -1733,6 +1735,7 @@ const schema = a.schema({
|
|
|
1733
1735
|
type: a.string().required(),
|
|
1734
1736
|
status: a.string().required(),
|
|
1735
1737
|
typeStatus: a.string().required(),
|
|
1738
|
+
stage: a.string(),
|
|
1736
1739
|
slug: a.string().required(),
|
|
1737
1740
|
shortSlug: a.string(),
|
|
1738
1741
|
section: a.string(),
|
|
@@ -1764,6 +1767,7 @@ const schema = a.schema({
|
|
|
1764
1767
|
index("typeStatus").sortKeys(["publishedAt"]).queryField("listItemsByTypeStatusAndPublishedAt"),
|
|
1765
1768
|
index("sectionStatus").sortKeys(["publishedAt"]).queryField("listItemsBySectionStatusAndPublishedAt"),
|
|
1766
1769
|
index("versionState").sortKeys(["updatedAt"]).queryField("listItemsByVersionStateAndUpdatedAt"),
|
|
1770
|
+
index("stage").sortKeys(["updatedAt"]).queryField("listItemsByStageAndUpdatedAt"),
|
|
1767
1771
|
])
|
|
1768
1772
|
.authorization((allow) => [
|
|
1769
1773
|
allow.groups(contentWriteGroups),
|
package/amplify/data/schema.js
CHANGED
|
@@ -799,9 +799,11 @@ const baseDefinitions = {
|
|
|
799
799
|
createdAt: a.datetime().required(),
|
|
800
800
|
updatedAt: a.datetime().required(),
|
|
801
801
|
newsroomFeedKey: a.string(),
|
|
802
|
+
itemLineageId: a.id(),
|
|
802
803
|
})
|
|
803
804
|
.secondaryIndexes((index) => [
|
|
804
805
|
index("newsroomFeedKey").sortKeys(["createdAt"]).queryField("listAssignmentsByNewsroomFeedAndCreatedAt"),
|
|
806
|
+
index("itemLineageId").sortKeys(["createdAt"]).queryField("listAssignmentsByItemLineageAndCreatedAt"),
|
|
805
807
|
index("queueStatusKey").sortKeys(["priority", "createdAt"]).queryField("listAssignmentsByQueueStatusAndPriority"),
|
|
806
808
|
index("assignmentTypeKey").sortKeys(["status", "createdAt"]).queryField("listAssignmentsByTypeStatusAndCreatedAt"),
|
|
807
809
|
index("status").sortKeys(["updatedAt"]).queryField("listAssignmentsByStatusAndUpdatedAt"),
|
|
@@ -1511,6 +1513,7 @@ const baseDefinitions = {
|
|
|
1511
1513
|
changeReason: a.string(),
|
|
1512
1514
|
contentHash: a.string(),
|
|
1513
1515
|
type: a.string().required(),
|
|
1516
|
+
stage: a.string(),
|
|
1514
1517
|
status: a.string().required(),
|
|
1515
1518
|
typeStatus: a.string().required(),
|
|
1516
1519
|
slug: a.string().required(),
|
|
@@ -1544,6 +1547,7 @@ const baseDefinitions = {
|
|
|
1544
1547
|
index("typeStatus").sortKeys(["publishedAt"]).queryField("listItemsByTypeStatusAndPublishedAt"),
|
|
1545
1548
|
index("sectionStatus").sortKeys(["publishedAt"]).queryField("listItemsBySectionStatusAndPublishedAt"),
|
|
1546
1549
|
index("versionState").sortKeys(["updatedAt"]).queryField("listItemsByVersionStateAndUpdatedAt"),
|
|
1550
|
+
index("stage").sortKeys(["updatedAt"]).queryField("listItemsByStageAndUpdatedAt"),
|
|
1547
1551
|
])
|
|
1548
1552
|
.authorization((allow) => [
|
|
1549
1553
|
allow.groups(contentWriteGroups),
|
package/amplify/data/schema.ts
CHANGED
|
@@ -870,9 +870,11 @@ const baseDefinitions = {
|
|
|
870
870
|
createdAt: a.datetime().required(),
|
|
871
871
|
updatedAt: a.datetime().required(),
|
|
872
872
|
newsroomFeedKey: a.string(),
|
|
873
|
+
itemLineageId: a.id(),
|
|
873
874
|
})
|
|
874
875
|
.secondaryIndexes((index) => [
|
|
875
876
|
index("newsroomFeedKey").sortKeys(["createdAt"]).queryField("listAssignmentsByNewsroomFeedAndCreatedAt"),
|
|
877
|
+
index("itemLineageId").sortKeys(["createdAt"]).queryField("listAssignmentsByItemLineageAndCreatedAt"),
|
|
876
878
|
index("queueStatusKey").sortKeys(["priority", "createdAt"]).queryField("listAssignmentsByQueueStatusAndPriority"),
|
|
877
879
|
index("assignmentTypeKey").sortKeys(["status", "createdAt"]).queryField("listAssignmentsByTypeStatusAndCreatedAt"),
|
|
878
880
|
index("status").sortKeys(["updatedAt"]).queryField("listAssignmentsByStatusAndUpdatedAt"),
|
|
@@ -1606,6 +1608,7 @@ const baseDefinitions = {
|
|
|
1606
1608
|
changeReason: a.string(),
|
|
1607
1609
|
contentHash: a.string(),
|
|
1608
1610
|
type: a.string().required(),
|
|
1611
|
+
stage: a.string(),
|
|
1609
1612
|
status: a.string().required(),
|
|
1610
1613
|
typeStatus: a.string().required(),
|
|
1611
1614
|
slug: a.string().required(),
|
|
@@ -1639,6 +1642,7 @@ const baseDefinitions = {
|
|
|
1639
1642
|
index("typeStatus").sortKeys(["publishedAt"]).queryField("listItemsByTypeStatusAndPublishedAt"),
|
|
1640
1643
|
index("sectionStatus").sortKeys(["publishedAt"]).queryField("listItemsBySectionStatusAndPublishedAt"),
|
|
1641
1644
|
index("versionState").sortKeys(["updatedAt"]).queryField("listItemsByVersionStateAndUpdatedAt"),
|
|
1645
|
+
index("stage").sortKeys(["updatedAt"]).queryField("listItemsByStageAndUpdatedAt"),
|
|
1642
1646
|
])
|
|
1643
1647
|
.authorization((allow) => [
|
|
1644
1648
|
allow.groups(contentWriteGroups),
|
package/amplify/site-backend.js
CHANGED
|
@@ -17,7 +17,8 @@ import { assertValidPublicationConfig } from "./site-backend-config.js";
|
|
|
17
17
|
import { revalidateSecretReadStatement } from "./revalidate-secret-policy.js";
|
|
18
18
|
import { revalidateDistributionEnvironment, revalidateInvalidationStatement, validateRevalidateDistributionIds, } from "./revalidate-invalidation-settings.js";
|
|
19
19
|
import { addInvalidationPathAlarm } from "./publication/invalidation-alarm.js";
|
|
20
|
-
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth } from "./auth/resource.js";
|
|
20
|
+
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth, siteAuthRedirectUrls } from "./auth/resource.js";
|
|
21
|
+
import { PROSPECTUS_SIGN_IN_CALLBACK_PATH, addProspectusGoogleOnlyClient } from "./auth/prospectus-google-client.js";
|
|
21
22
|
import { defineSiteData } from "./data/resource.js";
|
|
22
23
|
import { resolvePlugins } from "./plugins/registry.js";
|
|
23
24
|
import { wirePlugins } from "./plugins/wire.js";
|
|
@@ -107,6 +108,9 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
107
108
|
&& authConfig.disableGoogleOAuth !== true) {
|
|
108
109
|
applyCognitoDomainPrefix(backend.auth.stack, requestedCognitoDomainPrefix);
|
|
109
110
|
}
|
|
111
|
+
const prospectusGoogleOnlyClient = authConfig.disableGoogleOAuth === true
|
|
112
|
+
? undefined
|
|
113
|
+
: addProspectusGoogleOnlyClient(backend.auth.stack, backend.auth.resources.userPool, siteAuthRedirectUrls(authConfig));
|
|
110
114
|
const amplifyBackendDir = dirname(fileURLToPath(import.meta.url));
|
|
111
115
|
// Repo root in a Papyrus checkout; package root when installed from npm.
|
|
112
116
|
const projectRoot = resolve(amplifyBackendDir, "..");
|
|
@@ -546,6 +550,9 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
546
550
|
}));
|
|
547
551
|
backend.addOutput({
|
|
548
552
|
custom: {
|
|
553
|
+
prospectusSignIn: prospectusGoogleOnlyClient
|
|
554
|
+
? { clientId: prospectusGoogleOnlyClient.ref, callbackPath: PROSPECTUS_SIGN_IN_CALLBACK_PATH }
|
|
555
|
+
: null,
|
|
549
556
|
inboundEmail: enableInboundEmail
|
|
550
557
|
? {
|
|
551
558
|
domain: inboundEmailDomain,
|
package/amplify/site-backend.ts
CHANGED
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
validateRevalidateDistributionIds,
|
|
25
25
|
} from "./revalidate-invalidation-settings";
|
|
26
26
|
import { addInvalidationPathAlarm } from "./publication/invalidation-alarm";
|
|
27
|
-
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth } from "./auth/resource";
|
|
27
|
+
import { applyCognitoDomainPrefix, authConfigFromEnv, defineSiteAuth, siteAuthRedirectUrls } from "./auth/resource";
|
|
28
|
+
import { PROSPECTUS_SIGN_IN_CALLBACK_PATH, addProspectusGoogleOnlyClient } from "./auth/prospectus-google-client";
|
|
28
29
|
import { defineSiteData } from "./data/resource";
|
|
29
30
|
import { resolvePlugins } from "./plugins/registry";
|
|
30
31
|
import { wirePlugins } from "./plugins/wire";
|
|
@@ -137,6 +138,10 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
137
138
|
applyCognitoDomainPrefix(backend.auth.stack, requestedCognitoDomainPrefix);
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
const prospectusGoogleOnlyClient = authConfig.disableGoogleOAuth === true
|
|
142
|
+
? undefined
|
|
143
|
+
: addProspectusGoogleOnlyClient(backend.auth.stack, backend.auth.resources.userPool, siteAuthRedirectUrls(authConfig));
|
|
144
|
+
|
|
140
145
|
const amplifyBackendDir = dirname(fileURLToPath(import.meta.url));
|
|
141
146
|
// Repo root in a Papyrus checkout; package root when installed from npm.
|
|
142
147
|
const projectRoot = resolve(amplifyBackendDir, "..");
|
|
@@ -682,6 +687,9 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
682
687
|
|
|
683
688
|
backend.addOutput({
|
|
684
689
|
custom: {
|
|
690
|
+
prospectusSignIn: prospectusGoogleOnlyClient
|
|
691
|
+
? { clientId: prospectusGoogleOnlyClient.ref, callbackPath: PROSPECTUS_SIGN_IN_CALLBACK_PATH }
|
|
692
|
+
: null,
|
|
685
693
|
inboundEmail: enableInboundEmail
|
|
686
694
|
? {
|
|
687
695
|
domain: inboundEmailDomain,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleProspectusSignInCallback } from "../../../../../lib/prospectus-sign-in-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function GET(request: NextRequest) {
|
|
8
|
+
return handleProspectusSignInCallback(request);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleProspectusAccountSelection } from "../../../../../lib/prospectus-sign-in-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function POST(request: NextRequest) {
|
|
8
|
+
return handleProspectusAccountSelection(request);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleProspectusSessionRead } from "../../../../../lib/prospectus-sign-in-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function GET(request: NextRequest) {
|
|
8
|
+
return handleProspectusSessionRead(request);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleProspectusSignInStart } from "../../../../../lib/prospectus-sign-in-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function GET(request: NextRequest) {
|
|
8
|
+
return handleProspectusSignInStart(request);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { handleProspectusSignOut } from "../../../../../lib/prospectus-sign-in-routes";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const runtime = "nodejs";
|
|
6
|
+
|
|
7
|
+
export function POST(request: NextRequest) {
|
|
8
|
+
return handleProspectusSignOut(request);
|
|
9
|
+
}
|
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
UserDirectoryEntry,
|
|
40
40
|
} from "../lib/category-repository";
|
|
41
41
|
import { relationTypeKey } from "../lib/semantic-graph";
|
|
42
|
+
import { CONTENT_ITEM_STAGES, type ContentItemVersionRecord } from "../lib/content-item-lifecycle";
|
|
42
43
|
import { DOCTRINE_DEFINITIONS, getCategoryDoctrineDefinitions, type DoctrineCategory } from "../lib/doctrine";
|
|
43
44
|
import { configureAmplifyClient } from "./amplify-client-provider";
|
|
44
45
|
import { isUnauthenticatedError, loadReaderSessionSnapshot, type ReaderAuthSnapshot } from "./reader-auth-state";
|
|
@@ -253,7 +254,7 @@ const UPDATE_MESSAGE_MUTATION = `
|
|
|
253
254
|
const NEWSROOM_ASSIGNMENT_FEED_QUERY = `
|
|
254
255
|
query ListAssignmentsByNewsroomFeedAndCreatedAt($newsroomFeedKey: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelAssignmentFilterInput) {
|
|
255
256
|
listAssignmentsByNewsroomFeedAndCreatedAt(newsroomFeedKey: $newsroomFeedKey, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
256
|
-
items { id assignmentTypeKey queueKey queueStatusKey status priority title summary assigneeType assigneeId assigneeKey claimedAt claimExpiresAt completedAt canceledAt corpusId categorySetId classifierId sectionId sectionKey sectionType sectionStatusKey sectionQueueStatusKey primaryFocusCategoryKey topicScopeCategoryKeys sourceSnapshotId importRunId createdBy createdAt updatedAt newsroomFeedKey }
|
|
257
|
+
items { id assignmentTypeKey queueKey queueStatusKey status priority title summary assigneeType assigneeId assigneeKey claimedAt claimExpiresAt completedAt canceledAt corpusId categorySetId classifierId sectionId sectionKey sectionType sectionStatusKey sectionQueueStatusKey primaryFocusCategoryKey topicScopeCategoryKeys sourceSnapshotId importRunId createdBy createdAt updatedAt newsroomFeedKey itemLineageId }
|
|
257
258
|
nextToken
|
|
258
259
|
}
|
|
259
260
|
}
|
|
@@ -2157,6 +2158,41 @@ export async function loadNewsroomAssignmentPage(options: NewsroomAssignmentPage
|
|
|
2157
2158
|
});
|
|
2158
2159
|
}
|
|
2159
2160
|
|
|
2161
|
+
const CONTENT_ITEMS_BY_STAGE_QUERY = `
|
|
2162
|
+
query ListItemsByStageAndUpdatedAt($stage: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
2163
|
+
listItemsByStageAndUpdatedAt(stage: $stage, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
2164
|
+
items { id lineageId versionNumber stage type status title headline slug section updatedAt }
|
|
2165
|
+
nextToken
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
`;
|
|
2169
|
+
|
|
2170
|
+
export async function loadNewsroomContentItemVersions(): Promise<ContentItemVersionRecord[]> {
|
|
2171
|
+
configureAmplifyClient();
|
|
2172
|
+
const client = generateClient<Schema>() as unknown as {
|
|
2173
|
+
graphql: (options: Record<string, unknown>) => Promise<GraphQLConnectionResponse<ContentItemVersionRecord>>;
|
|
2174
|
+
};
|
|
2175
|
+
const field = "listItemsByStageAndUpdatedAt";
|
|
2176
|
+
const versionsPerStage = await Promise.all(CONTENT_ITEM_STAGES.map(async (stage) => {
|
|
2177
|
+
const versions: ContentItemVersionRecord[] = [];
|
|
2178
|
+
let nextToken: string | null = null;
|
|
2179
|
+
do {
|
|
2180
|
+
const response: GraphQLConnectionResponse<ContentItemVersionRecord> = await client.graphql({
|
|
2181
|
+
query: CONTENT_ITEMS_BY_STAGE_QUERY,
|
|
2182
|
+
variables: { stage, sortDirection: "DESC", limit: NEWSROOM_PAGE_LIMIT, nextToken },
|
|
2183
|
+
authMode: USER_POOL_AUTH_MODE,
|
|
2184
|
+
});
|
|
2185
|
+
assertNoGraphQLErrors(response.errors);
|
|
2186
|
+
const connection = response.data?.[field];
|
|
2187
|
+
if (!connection) throw new Error(`Missing GraphQL connection payload for ${field}.`);
|
|
2188
|
+
versions.push(...((connection.items ?? []).filter(Boolean) as ContentItemVersionRecord[]));
|
|
2189
|
+
nextToken = connection.nextToken ?? null;
|
|
2190
|
+
} while (nextToken);
|
|
2191
|
+
return versions;
|
|
2192
|
+
}));
|
|
2193
|
+
return versionsPerStage.flat();
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2160
2196
|
export async function loadModelPayloadsForOwner(
|
|
2161
2197
|
ownerKind: string,
|
|
2162
2198
|
ownerId: string,
|
|
@@ -6,10 +6,9 @@ import { IssueCollection, IssueDetailPanel, IssueTypeFilter, issueTypeFromFilter
|
|
|
6
6
|
import "@kanbus/ui/styles/kanban.css";
|
|
7
7
|
import "./newsroom-newsprint-board.css";
|
|
8
8
|
import { canReadNewsroomBoard, newsroomBoardConfig, projectNewsroomBoard, type NativeBoardIssue } from "../lib/newsroom-board";
|
|
9
|
-
import {
|
|
10
|
-
import { SITE_BRAND } from "../lib/site-brand";
|
|
9
|
+
import { getContentItemName, SITE_BRAND } from "../lib/site-brand";
|
|
11
10
|
import { getNewsroomNavHref } from "../lib/newsroom-nav";
|
|
12
|
-
import { loadNewsroomAssignmentPage, loadNewsroomReferencePage } from "./news-desk-taxonomy-client";
|
|
11
|
+
import { loadNewsroomAssignmentPage, loadNewsroomContentItemVersions, loadNewsroomReferencePage } from "./news-desk-taxonomy-client";
|
|
13
12
|
import { useOptionalNewsDeskClient } from "./news-desk-client-provider";
|
|
14
13
|
import { NewsDeskAccessGate } from "./newsroom-access-gate";
|
|
15
14
|
import { NewsroomOpsShell, NewsroomOpsStatusBanner } from "./newsroom-ops-shell";
|
|
@@ -45,12 +44,12 @@ export function NewsroomBoardView() {
|
|
|
45
44
|
if (!allowed) { setLoading(false); return; }
|
|
46
45
|
setLoading(true); busy.current = true;
|
|
47
46
|
Promise.all([
|
|
48
|
-
|
|
47
|
+
loadNewsroomContentItemVersions(),
|
|
49
48
|
loadNewsroomReferencePage({ limit: 50 }),
|
|
50
49
|
loadNewsroomAssignmentPage({ limit: 50 }),
|
|
51
|
-
]).then(([
|
|
50
|
+
]).then(([contentItemVersions, references, assignments]) => {
|
|
52
51
|
if (generation.current !== request) return;
|
|
53
|
-
setIssues(projectNewsroomBoard({
|
|
52
|
+
setIssues(projectNewsroomBoard({ contentItemVersions, references: references.items, assignments: assignments.items }));
|
|
54
53
|
setTokens({ reference: references.nextToken, assignment: assignments.nextToken });
|
|
55
54
|
}).catch((reason: unknown) => {
|
|
56
55
|
if (generation.current === request) setError(reason instanceof Error ? reason.message : "Could not load newsroom records.");
|
|
@@ -81,7 +80,7 @@ export function NewsroomBoardView() {
|
|
|
81
80
|
<IssueTypeFilter config={config} value={filter} onChange={value => { setFilter(value); setSelected(null); }} name="Record type" />
|
|
82
81
|
<div role="group" aria-label="Record presentation"><button type="button" aria-pressed={view === "board"} onClick={() => setView("board")}>Board</button><button type="button" aria-pressed={view === "list"} onClick={() => setView("list")}>List</button><button type="button" disabled={loading} onClick={() => setRevision(current => current + 1)}>Refresh records</button></div>
|
|
83
82
|
</div>
|
|
84
|
-
<p className="organizer-board-note">
|
|
83
|
+
<p className="organizer-board-note">{`Each ${getContentItemName().singular.toLowerCase()} appears once, in the column for its stage. Open a record to use its existing editor or workflow controls.`}</p>
|
|
85
84
|
{error && <NewsroomOpsStatusBanner tone="error">{error}</NewsroomOpsStatusBanner>}
|
|
86
85
|
<p role="status" className="organizer-board-note">{loading ? "Loading records…" : `${issues.length} records loaded. References and assignments load in pages.`}</p>
|
|
87
86
|
<div className="organizer-board-body" data-detail-open={Boolean(issue)}>
|
|
@@ -91,7 +90,7 @@ export function NewsroomBoardView() {
|
|
|
91
90
|
selectedIssueId={selected} detailOpen={Boolean(issue)} onSelectIssue={item => setSelected(item.id)} ariaLabel="Newsroom records"
|
|
92
91
|
emptyMessage={loading ? "Loading records…" : error ? "Records could not be loaded." : "No records in this selection."} />
|
|
93
92
|
{issue && <IssueDetailPanel issue={issue} onClose={() => setSelected(null)} fields={[{ label: "CMS record ID", value: issue.nativeId }, { label: "Status", value: issue.nativeStatus }]}>
|
|
94
|
-
<Link href={getNewsroomNavHref(issue.href)}>Open {issue.type}
|
|
93
|
+
<Link href={getNewsroomNavHref(issue.href)}>Open {issue.type}</Link>
|
|
95
94
|
</IssueDetailPanel>}
|
|
96
95
|
</div>
|
|
97
96
|
<div className="organizer-board-pages">{tokens.reference && <button disabled={loading} type="button" onClick={() => void loadMore("reference")}>Load more references</button>}{tokens.assignment && <button disabled={loading} type="button" onClick={() => void loadMore("assignment")}>Load more assignments</button>}</div>
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type PrimingProofReport,
|
|
5
5
|
} from "../lib/priming-proof-report";
|
|
6
6
|
|
|
7
|
-
function ProofMethod({ heldOutSize }: { heldOutSize: number | null }) {
|
|
7
|
+
function ProofMethod({ heldOutSize, teachingSource }: { heldOutSize: number | null; teachingSource: string | null }) {
|
|
8
8
|
return (
|
|
9
9
|
<section aria-labelledby="priming-proof-method" className="space-y-2 text-sm leading-relaxed">
|
|
10
10
|
<h2 id="priming-proof-method" className="text-base font-semibold uppercase tracking-wide">How the number is made</h2>
|
|
@@ -14,8 +14,10 @@ function ProofMethod({ heldOutSize }: { heldOutSize: number | null }) {
|
|
|
14
14
|
for teaching. A classifier that always gives the same answer agrees on half of it.
|
|
15
15
|
</p>
|
|
16
16
|
<p>
|
|
17
|
-
Editors teach the classifier
|
|
18
|
-
|
|
17
|
+
Editors teach the classifier with labels: their other reviews of candidate sources, and sources they paste,
|
|
18
|
+
check after Papyrus extracts them, and mark publish or reject.
|
|
19
|
+
{teachingSource === null ? "" : ` This run taught from ${teachingSource}.`} Each point below starts a fresh
|
|
20
|
+
classifier that sees only the first N of those labels, then decides
|
|
19
21
|
every held-out source once. Nothing else changes between points: the same decision model, the publication's
|
|
20
22
|
mission and policies as the only rubric, and no automatic rubric rewriting.
|
|
21
23
|
</p>
|
|
@@ -31,7 +33,7 @@ export function PrimingProofPage({ report }: { report: PrimingProofReport | null
|
|
|
31
33
|
<h1 className="text-3xl font-bold">Does teaching the classifier work?</h1>
|
|
32
34
|
<p className="text-muted-foreground">No approved proof run has been published for this publication yet.</p>
|
|
33
35
|
</header>
|
|
34
|
-
<ProofMethod heldOutSize={null} />
|
|
36
|
+
<ProofMethod heldOutSize={null} teachingSource={null} />
|
|
35
37
|
</main>
|
|
36
38
|
);
|
|
37
39
|
}
|
|
@@ -82,7 +84,7 @@ export function PrimingProofPage({ report }: { report: PrimingProofReport | null
|
|
|
82
84
|
</table>
|
|
83
85
|
</div>
|
|
84
86
|
</section>
|
|
85
|
-
<ProofMethod heldOutSize={report.heldOut.size} />
|
|
87
|
+
<ProofMethod heldOutSize={report.heldOut.size} teachingSource={report.priming.source} />
|
|
86
88
|
<p className="break-all text-xs text-muted-foreground">
|
|
87
89
|
Held-out fingerprint {report.heldOut.fingerprint}
|
|
88
90
|
{report.priming.fingerprint ? ` · labels fingerprint ${report.priming.fingerprint}` : ""}
|
|
@@ -15042,6 +15042,9 @@ function buildUiCopywritingAssignment({ actorLabel, assignment, decision, messag
|
|
|
15042
15042
|
message: MessageRecord;
|
|
15043
15043
|
now: string;
|
|
15044
15044
|
}): AssignmentRecord {
|
|
15045
|
+
if (!assignment.itemLineageId) {
|
|
15046
|
+
throw new Error(`Assignment ${assignment.id} has no content item lineage; copywriting must continue the same content item.`);
|
|
15047
|
+
}
|
|
15045
15048
|
const type = decision === "brief" ? "brief" : "article";
|
|
15046
15049
|
const assignmentTypeKey = type === "brief" ? "copywriting.brief-draft" : "copywriting.article-draft";
|
|
15047
15050
|
const section = assignment.sectionKey ?? assignment.sectionId ?? "unsectioned";
|
|
@@ -15094,6 +15097,7 @@ function buildUiCopywritingAssignment({ actorLabel, assignment, decision, messag
|
|
|
15094
15097
|
createdAt: now,
|
|
15095
15098
|
updatedAt: now,
|
|
15096
15099
|
newsroomFeedKey: "assignment#open",
|
|
15100
|
+
itemLineageId: assignment.itemLineageId,
|
|
15097
15101
|
metadata,
|
|
15098
15102
|
};
|
|
15099
15103
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ArnFormat, CfnOutput, Duration, Fn, Stack } from "aws-cdk-lib";
|
|
2
2
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
3
3
|
import * as amplify from "aws-cdk-lib/aws-amplify";
|
|
4
|
+
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
|
|
4
5
|
import { GITHUB_OIDC_PROVIDER_HOST } from "./github-oidc-provider.js";
|
|
5
6
|
import { cmsProductionBuildSpec, cmsStagingBuildSpec, readerBuildSpec } from "./build-specs.js";
|
|
6
7
|
import { PROSPECTUS_PAPYRUS_SITE_ROUTES, hasCmsApp, isStagingEnabled, resolveRedirectSubDomainPrefix, resolveCmsHostName, resolveProspectusAccountsEndpoint, resolveRevalidateSecretParameterName, resolveStackName, resolveStagingDomainName, resolveStoragePreviewPrefix } from "./site-config.js";
|
|
@@ -86,9 +87,26 @@ export class AmplifyAppShellStack extends Stack {
|
|
|
86
87
|
}));
|
|
87
88
|
}
|
|
88
89
|
const prospectusAccountsApiStageArn = config.cms.prospectusAccountsApiStageArn;
|
|
89
|
-
const
|
|
90
|
-
?
|
|
90
|
+
const prospectusSessionSealingSecret = prospectusAccountsApiStageArn
|
|
91
|
+
? new secretsmanager.Secret(this, "ProspectusSessionSealingSecret", {
|
|
92
|
+
description: `Seals the Prospectus sign-in cookies of ${cmsAppName}; replacing it signs everyone out`,
|
|
93
|
+
generateSecretString: { passwordLength: 64, excludePunctuation: true },
|
|
94
|
+
})
|
|
95
|
+
: undefined;
|
|
96
|
+
const prospectusEnvironment = prospectusAccountsApiStageArn && prospectusSessionSealingSecret
|
|
97
|
+
? {
|
|
98
|
+
PAPYRUS_PROSPECTUS_ACCOUNTS_ENDPOINT: resolveProspectusAccountsEndpoint(prospectusAccountsApiStageArn),
|
|
99
|
+
PAPYRUS_PROSPECTUS_SESSION_SECRET_ARN: prospectusSessionSealingSecret.secretArn,
|
|
100
|
+
PAPYRUS_PROSPECTUS_SITE_PRODUCT: `papyrus-${config.siteId}`,
|
|
101
|
+
}
|
|
91
102
|
: {};
|
|
103
|
+
if (prospectusSessionSealingSecret) {
|
|
104
|
+
computeRole.addToPolicy(new iam.PolicyStatement({
|
|
105
|
+
sid: "ReadProspectusSessionSealingSecret",
|
|
106
|
+
actions: ["secretsmanager:GetSecretValue"],
|
|
107
|
+
resources: [prospectusSessionSealingSecret.secretArn],
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
92
110
|
if (prospectusAccountsApiStageArn) {
|
|
93
111
|
computeRole.addToPolicy(new iam.PolicyStatement({
|
|
94
112
|
sid: "InvokeProspectusAccountsAsRegisteredSite",
|