@anthusai/papyrus 3.1.0 → 3.3.0
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/data/resource.js +7 -0
- package/amplify/data/resource.ts +8 -0
- package/amplify/data/schema.js +8 -0
- package/amplify/data/schema.ts +9 -0
- package/amplify/functions/assignment-action/data-access.js +88 -0
- package/amplify/functions/assignment-action/data-access.ts +87 -0
- package/amplify/functions/content-actions/handler.py +7 -0
- package/amplify/plugins/editorial-checks/function/handler.py +4 -1
- package/amplify/plugins/editorial-checks/papyrus.plugin.js +23 -2
- package/amplify/plugins/editorial-checks/papyrus.plugin.ts +26 -2
- package/amplify/site-backend.js +5 -0
- package/amplify/site-backend.ts +11 -0
- package/package.json +1 -1
- package/routes.manifest.json +1 -1
package/amplify/data/resource.js
CHANGED
|
@@ -589,6 +589,12 @@ const schema = a.schema({
|
|
|
589
589
|
.returns(a.json())
|
|
590
590
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
591
591
|
.handler(a.handler.function(contentActions)),
|
|
592
|
+
intakePendingReferences: a
|
|
593
|
+
.mutation()
|
|
594
|
+
.arguments({ input: a.json().required() })
|
|
595
|
+
.returns(a.json())
|
|
596
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
597
|
+
.handler(a.handler.function(contentActions)),
|
|
592
598
|
saveItemDraft: a
|
|
593
599
|
.mutation()
|
|
594
600
|
.arguments({ input: a.json().required() })
|
|
@@ -2146,6 +2152,7 @@ const schema = a.schema({
|
|
|
2146
2152
|
allow.groups(contentWriteGroups),
|
|
2147
2153
|
]),
|
|
2148
2154
|
}).authorization((allow) => [
|
|
2155
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2149
2156
|
allow.resource(categoryAction),
|
|
2150
2157
|
allow.resource(contentActions),
|
|
2151
2158
|
allow.resource(manageUserRole),
|
package/amplify/data/resource.ts
CHANGED
|
@@ -641,6 +641,13 @@ const schema = a.schema({
|
|
|
641
641
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
642
642
|
.handler(a.handler.function(contentActions)),
|
|
643
643
|
|
|
644
|
+
intakePendingReferences: a
|
|
645
|
+
.mutation()
|
|
646
|
+
.arguments({ input: a.json().required() })
|
|
647
|
+
.returns(a.json())
|
|
648
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
649
|
+
.handler(a.handler.function(contentActions)),
|
|
650
|
+
|
|
644
651
|
saveItemDraft: a
|
|
645
652
|
.mutation()
|
|
646
653
|
.arguments({ input: a.json().required() })
|
|
@@ -2271,6 +2278,7 @@ const schema = a.schema({
|
|
|
2271
2278
|
allow.groups(contentWriteGroups),
|
|
2272
2279
|
]),
|
|
2273
2280
|
}).authorization((allow) => [
|
|
2281
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2274
2282
|
allow.resource(categoryAction),
|
|
2275
2283
|
allow.resource(contentActions),
|
|
2276
2284
|
allow.resource(manageUserRole),
|
package/amplify/data/schema.js
CHANGED
|
@@ -496,6 +496,12 @@ const baseDefinitions = {
|
|
|
496
496
|
.returns(a.json())
|
|
497
497
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
498
498
|
.handler(a.handler.function(contentActions)),
|
|
499
|
+
intakePendingReferences: a
|
|
500
|
+
.mutation()
|
|
501
|
+
.arguments({ input: a.json().required() })
|
|
502
|
+
.returns(a.json())
|
|
503
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
504
|
+
.handler(a.handler.function(contentActions)),
|
|
499
505
|
saveItemDraft: a
|
|
500
506
|
.mutation()
|
|
501
507
|
.arguments({ input: a.json().required() })
|
|
@@ -1989,6 +1995,7 @@ const baseDefinitions = {
|
|
|
1989
1995
|
]),
|
|
1990
1996
|
};
|
|
1991
1997
|
export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
1998
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
1992
1999
|
allow.resource(categoryAction),
|
|
1993
2000
|
allow.resource(contentActions),
|
|
1994
2001
|
allow.resource(manageUserRole),
|
|
@@ -1999,6 +2006,7 @@ export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
|
1999
2006
|
/** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
|
|
2000
2007
|
export function schemaWithPlugins(pluginDefinitions) {
|
|
2001
2008
|
return a.schema({ ...baseDefinitions, ...pluginDefinitions }).authorization((allow) => [
|
|
2009
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2002
2010
|
allow.resource(categoryAction),
|
|
2003
2011
|
allow.resource(contentActions),
|
|
2004
2012
|
allow.resource(manageUserRole),
|
package/amplify/data/schema.ts
CHANGED
|
@@ -542,6 +542,13 @@ const baseDefinitions = {
|
|
|
542
542
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
543
543
|
.handler(a.handler.function(contentActions)),
|
|
544
544
|
|
|
545
|
+
intakePendingReferences: a
|
|
546
|
+
.mutation()
|
|
547
|
+
.arguments({ input: a.json().required() })
|
|
548
|
+
.returns(a.json())
|
|
549
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
550
|
+
.handler(a.handler.function(contentActions)),
|
|
551
|
+
|
|
545
552
|
saveItemDraft: a
|
|
546
553
|
.mutation()
|
|
547
554
|
.arguments({ input: a.json().required() })
|
|
@@ -2108,6 +2115,7 @@ const baseDefinitions = {
|
|
|
2108
2115
|
};
|
|
2109
2116
|
|
|
2110
2117
|
export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
2118
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2111
2119
|
allow.resource(categoryAction),
|
|
2112
2120
|
allow.resource(contentActions),
|
|
2113
2121
|
allow.resource(manageUserRole),
|
|
@@ -2119,6 +2127,7 @@ export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
|
2119
2127
|
/** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
|
|
2120
2128
|
export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
|
|
2121
2129
|
return a.schema({ ...baseDefinitions, ...pluginDefinitions } as typeof baseDefinitions).authorization((allow) => [
|
|
2130
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2122
2131
|
allow.resource(categoryAction),
|
|
2123
2132
|
allow.resource(contentActions),
|
|
2124
2133
|
allow.resource(manageUserRole),
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Aspects, Stack } from "aws-cdk-lib";
|
|
2
|
+
import { CfnPolicy } from "aws-cdk-lib/aws-iam";
|
|
3
|
+
/** Lifecycle/context calls, including the fixed reference registration CLI's provenance operations. */
|
|
4
|
+
export const ASSIGNMENT_DATA_FIELDS = [
|
|
5
|
+
"Query/fields/getAssignment",
|
|
6
|
+
"Query/fields/getKnowledgeRawPayload",
|
|
7
|
+
"Query/fields/getProcedureRun",
|
|
8
|
+
"Query/fields/getProcedureVersion",
|
|
9
|
+
"Query/fields/getProcedureDefinition",
|
|
10
|
+
"Query/fields/getModelAttachment",
|
|
11
|
+
"Query/fields/getNewsroomSection",
|
|
12
|
+
"Query/fields/getKnowledgeCorpus",
|
|
13
|
+
"Query/fields/getKnowledgeImportRun",
|
|
14
|
+
"Query/fields/getReference",
|
|
15
|
+
"Query/fields/getReferenceAttachment",
|
|
16
|
+
"Query/fields/getMessage",
|
|
17
|
+
"Query/fields/getSemanticRelation",
|
|
18
|
+
"Query/fields/getSemanticNode",
|
|
19
|
+
"Query/fields/listReferences",
|
|
20
|
+
"Query/fields/listReferenceAttachments",
|
|
21
|
+
"Query/fields/listSemanticRelations",
|
|
22
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
23
|
+
"Query/fields/listItemsByLineageAndVersion",
|
|
24
|
+
"Query/fields/itemBySlug",
|
|
25
|
+
"Query/fields/itemByTenantSlug",
|
|
26
|
+
"Query/fields/listModelAttachmentsByOwnerRoleAndSortKey",
|
|
27
|
+
"Query/fields/listAssignmentsByQueueStatusAndPriority",
|
|
28
|
+
"Query/fields/listSemanticRelationsByObjectState",
|
|
29
|
+
"Query/fields/listSemanticRelationsBySubjectState",
|
|
30
|
+
"Query/fields/listAssignmentEventsByAssignmentAndCreatedAt",
|
|
31
|
+
"Mutation/fields/createAssignment",
|
|
32
|
+
"Mutation/fields/updateAssignment",
|
|
33
|
+
"Mutation/fields/createAssignmentEvent",
|
|
34
|
+
"Mutation/fields/createProcedureRun",
|
|
35
|
+
"Mutation/fields/updateProcedureRun",
|
|
36
|
+
"Mutation/fields/createKnowledgeCorpus",
|
|
37
|
+
"Mutation/fields/updateKnowledgeCorpus",
|
|
38
|
+
"Mutation/fields/createKnowledgeImportRun",
|
|
39
|
+
"Mutation/fields/updateKnowledgeImportRun",
|
|
40
|
+
"Mutation/fields/createKnowledgeRawPayload",
|
|
41
|
+
"Mutation/fields/updateKnowledgeRawPayload",
|
|
42
|
+
"Mutation/fields/createReference",
|
|
43
|
+
"Mutation/fields/updateReference",
|
|
44
|
+
"Mutation/fields/createReferenceAttachment",
|
|
45
|
+
"Mutation/fields/updateReferenceAttachment",
|
|
46
|
+
"Mutation/fields/createMessage",
|
|
47
|
+
"Mutation/fields/updateMessage",
|
|
48
|
+
"Mutation/fields/createSemanticRelation",
|
|
49
|
+
"Mutation/fields/updateSemanticRelation",
|
|
50
|
+
"Mutation/fields/createSemanticNode",
|
|
51
|
+
"Mutation/fields/updateSemanticNode",
|
|
52
|
+
"Mutation/fields/createModelAttachment",
|
|
53
|
+
"Mutation/fields/updateModelAttachment",
|
|
54
|
+
"Mutation/fields/createModelAttachmentUpload",
|
|
55
|
+
"Mutation/fields/completeModelAttachmentUpload",
|
|
56
|
+
"Mutation/fields/createModelAttachmentDownload",
|
|
57
|
+
"Mutation/fields/updateNewsroomSummary",
|
|
58
|
+
];
|
|
59
|
+
/** Keep the SDK's generated configuration, replacing only this role's GraphQL grants at synthesis. */
|
|
60
|
+
export function scopeAssignmentDataAccess(lambda, graphqlApiArn) {
|
|
61
|
+
if (!lambda.role)
|
|
62
|
+
throw new Error("Assignment function needs an execution role.");
|
|
63
|
+
const roleName = lambda.role.roleName;
|
|
64
|
+
const resources = ASSIGNMENT_DATA_FIELDS.map((field) => `${graphqlApiArn}/types/${field}`);
|
|
65
|
+
Aspects.of(Stack.of(lambda)).add({
|
|
66
|
+
visit(node) {
|
|
67
|
+
if (!(node instanceof CfnPolicy))
|
|
68
|
+
return;
|
|
69
|
+
const stack = Stack.of(node);
|
|
70
|
+
const roles = stack.resolve(node.roles);
|
|
71
|
+
if (!roles?.some((role) => JSON.stringify(role) === JSON.stringify(stack.resolve(roleName))))
|
|
72
|
+
return;
|
|
73
|
+
const document = stack.resolve(node.policyDocument);
|
|
74
|
+
document.Statement.forEach((statement, index) => {
|
|
75
|
+
const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action];
|
|
76
|
+
if (actions.includes("*") || actions.includes("appsync:*")) {
|
|
77
|
+
throw new Error("Unexpected wildcard assignment action; review permissions before synthesis.");
|
|
78
|
+
}
|
|
79
|
+
if (!actions.includes("appsync:GraphQL"))
|
|
80
|
+
return;
|
|
81
|
+
if (statement.Effect !== "Allow" || actions.length !== 1 || statement.NotResource) {
|
|
82
|
+
throw new Error("Unexpected assignment GraphQL policy; review field scoping before synthesis.");
|
|
83
|
+
}
|
|
84
|
+
node.addPropertyOverride(`PolicyDocument.Statement.${index}.Resource`, resources);
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Aspects, Stack } from "aws-cdk-lib";
|
|
2
|
+
import { CfnPolicy } from "aws-cdk-lib/aws-iam";
|
|
3
|
+
import type { IFunction } from "aws-cdk-lib/aws-lambda";
|
|
4
|
+
|
|
5
|
+
/** Lifecycle/context calls, including the fixed reference registration CLI's provenance operations. */
|
|
6
|
+
export const ASSIGNMENT_DATA_FIELDS = [
|
|
7
|
+
"Query/fields/getAssignment",
|
|
8
|
+
"Query/fields/getKnowledgeRawPayload",
|
|
9
|
+
"Query/fields/getProcedureRun",
|
|
10
|
+
"Query/fields/getProcedureVersion",
|
|
11
|
+
"Query/fields/getProcedureDefinition",
|
|
12
|
+
"Query/fields/getModelAttachment",
|
|
13
|
+
"Query/fields/getNewsroomSection",
|
|
14
|
+
"Query/fields/getKnowledgeCorpus",
|
|
15
|
+
"Query/fields/getKnowledgeImportRun",
|
|
16
|
+
"Query/fields/getReference",
|
|
17
|
+
"Query/fields/getReferenceAttachment",
|
|
18
|
+
"Query/fields/getMessage",
|
|
19
|
+
"Query/fields/getSemanticRelation",
|
|
20
|
+
"Query/fields/getSemanticNode",
|
|
21
|
+
"Query/fields/listReferences",
|
|
22
|
+
"Query/fields/listReferenceAttachments",
|
|
23
|
+
"Query/fields/listSemanticRelations",
|
|
24
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
25
|
+
"Query/fields/listItemsByLineageAndVersion",
|
|
26
|
+
"Query/fields/itemBySlug",
|
|
27
|
+
"Query/fields/itemByTenantSlug",
|
|
28
|
+
"Query/fields/listModelAttachmentsByOwnerRoleAndSortKey",
|
|
29
|
+
"Query/fields/listAssignmentsByQueueStatusAndPriority",
|
|
30
|
+
"Query/fields/listSemanticRelationsByObjectState",
|
|
31
|
+
"Query/fields/listSemanticRelationsBySubjectState",
|
|
32
|
+
"Query/fields/listAssignmentEventsByAssignmentAndCreatedAt",
|
|
33
|
+
"Mutation/fields/createAssignment",
|
|
34
|
+
"Mutation/fields/updateAssignment",
|
|
35
|
+
"Mutation/fields/createAssignmentEvent",
|
|
36
|
+
"Mutation/fields/createProcedureRun",
|
|
37
|
+
"Mutation/fields/updateProcedureRun",
|
|
38
|
+
"Mutation/fields/createKnowledgeCorpus",
|
|
39
|
+
"Mutation/fields/updateKnowledgeCorpus",
|
|
40
|
+
"Mutation/fields/createKnowledgeImportRun",
|
|
41
|
+
"Mutation/fields/updateKnowledgeImportRun",
|
|
42
|
+
"Mutation/fields/createKnowledgeRawPayload",
|
|
43
|
+
"Mutation/fields/updateKnowledgeRawPayload",
|
|
44
|
+
"Mutation/fields/createReference",
|
|
45
|
+
"Mutation/fields/updateReference",
|
|
46
|
+
"Mutation/fields/createReferenceAttachment",
|
|
47
|
+
"Mutation/fields/updateReferenceAttachment",
|
|
48
|
+
"Mutation/fields/createMessage",
|
|
49
|
+
"Mutation/fields/updateMessage",
|
|
50
|
+
"Mutation/fields/createSemanticRelation",
|
|
51
|
+
"Mutation/fields/updateSemanticRelation",
|
|
52
|
+
"Mutation/fields/createSemanticNode",
|
|
53
|
+
"Mutation/fields/updateSemanticNode",
|
|
54
|
+
"Mutation/fields/createModelAttachment",
|
|
55
|
+
"Mutation/fields/updateModelAttachment",
|
|
56
|
+
"Mutation/fields/createModelAttachmentUpload",
|
|
57
|
+
"Mutation/fields/completeModelAttachmentUpload",
|
|
58
|
+
"Mutation/fields/createModelAttachmentDownload",
|
|
59
|
+
"Mutation/fields/updateNewsroomSummary",
|
|
60
|
+
] as const;
|
|
61
|
+
|
|
62
|
+
/** Keep the SDK's generated configuration, replacing only this role's GraphQL grants at synthesis. */
|
|
63
|
+
export function scopeAssignmentDataAccess(lambda: IFunction, graphqlApiArn: string): void {
|
|
64
|
+
if (!lambda.role) throw new Error("Assignment function needs an execution role.");
|
|
65
|
+
const roleName = lambda.role.roleName;
|
|
66
|
+
const resources = ASSIGNMENT_DATA_FIELDS.map((field) => `${graphqlApiArn}/types/${field}`);
|
|
67
|
+
Aspects.of(Stack.of(lambda)).add({
|
|
68
|
+
visit(node) {
|
|
69
|
+
if (!(node instanceof CfnPolicy)) return;
|
|
70
|
+
const stack = Stack.of(node);
|
|
71
|
+
const roles = stack.resolve(node.roles) as unknown[] | undefined;
|
|
72
|
+
if (!roles?.some((role) => JSON.stringify(role) === JSON.stringify(stack.resolve(roleName)))) return;
|
|
73
|
+
const document = stack.resolve(node.policyDocument) as { Statement: Array<Record<string, unknown>> };
|
|
74
|
+
document.Statement.forEach((statement, index) => {
|
|
75
|
+
const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action];
|
|
76
|
+
if (actions.includes("*") || actions.includes("appsync:*")) {
|
|
77
|
+
throw new Error("Unexpected wildcard assignment action; review permissions before synthesis.");
|
|
78
|
+
}
|
|
79
|
+
if (!actions.includes("appsync:GraphQL")) return;
|
|
80
|
+
if (statement.Effect !== "Allow" || actions.length !== 1 || statement.NotResource) {
|
|
81
|
+
throw new Error("Unexpected assignment GraphQL policy; review field scoping before synthesis.");
|
|
82
|
+
}
|
|
83
|
+
node.addPropertyOverride(`PolicyDocument.Statement.${index}.Resource`, resources);
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -23,6 +23,7 @@ from papyrus_content.rebuild_trigger import (
|
|
|
23
23
|
trigger_staging_build,
|
|
24
24
|
)
|
|
25
25
|
from papyrus_content.story_reporting import save_story_version
|
|
26
|
+
from papyrus_content.reference_intake import IntakeError, intake_references
|
|
26
27
|
from papyrus_content.publishing import (
|
|
27
28
|
ItemFields,
|
|
28
29
|
PublishError,
|
|
@@ -299,6 +300,12 @@ def handler(event, context):
|
|
|
299
300
|
arguments = parse_input(event["arguments"]["input"])
|
|
300
301
|
actor = actor_from_event(event)
|
|
301
302
|
try:
|
|
303
|
+
if field == "intakePendingReferences":
|
|
304
|
+
client, _claims = create_authoring_client()
|
|
305
|
+
try:
|
|
306
|
+
return intake_references(client, arguments, event.get("identity") or {})
|
|
307
|
+
except IntakeError as error:
|
|
308
|
+
return {"ok": False, "errors": [{"code": "intake-rejected", "message": str(error)}]}
|
|
302
309
|
if field == "deriveMarkus":
|
|
303
310
|
return derive_markus_action(arguments)
|
|
304
311
|
if field == "saveItemDraft":
|
|
@@ -4,7 +4,10 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from papyrus_content.editorial_checks import GraphQLEditorialStore, Services, handle
|
|
7
|
+
from papyrus_content.editorial_checks import GraphQLEditorialStore, Services, handle, load_provider_keys
|
|
8
|
+
|
|
9
|
+
# Amplify passes secrets to Python functions as placeholders; resolve them once per cold start.
|
|
10
|
+
load_provider_keys()
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
def handler(event: dict[str, Any], _context: Any = None) -> dict[str, Any]:
|
|
@@ -7,12 +7,28 @@ export const EDITORIAL_CHECKS_QUERIES = ["checkEditorial", "suggestEditorialRewr
|
|
|
7
7
|
export const EDITORIAL_CHECKS_MUTATIONS = ["importEditorialStarterPack", "recalibrateEditorialPolicies"];
|
|
8
8
|
/** The model operations the function performs with its own role. */
|
|
9
9
|
export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
10
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
11
|
+
"Query/fields/getEditorialContext",
|
|
10
12
|
"Query/fields/listEditorialPolicies",
|
|
11
13
|
"Query/fields/getEditorialCheckSettings",
|
|
12
14
|
"Query/fields/listEditorialFeedbacks",
|
|
13
15
|
"Mutation/fields/createEditorialPolicy",
|
|
14
16
|
"Mutation/fields/updateEditorialPolicy",
|
|
15
17
|
];
|
|
18
|
+
export const EDITORIAL_CHECKS_SECRETS = ["TYPESAFE_API_KEY", "OPENAI_API_KEY"];
|
|
19
|
+
/**
|
|
20
|
+
* SSM parameters holding the provider keys. A Python function reads its secret()
|
|
21
|
+
* values itself (Amplify only resolves them for Node functions), so it needs read
|
|
22
|
+
* access to the app's branch and shared secret paths, and to the sandbox paths.
|
|
23
|
+
*/
|
|
24
|
+
export function editorialChecksSecretParameters(region, account, appId) {
|
|
25
|
+
const arn = (path) => `arn:aws:ssm:${region}:${account}:parameter/amplify/${path}`;
|
|
26
|
+
return EDITORIAL_CHECKS_SECRETS.flatMap((name) => [
|
|
27
|
+
arn(`papyrus/*/${name}`),
|
|
28
|
+
arn(`shared/papyrus/${name}`),
|
|
29
|
+
...(appId ? [arn(`${appId}/*/${name}`), arn(`shared/${appId}/${name}`)] : []),
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
16
32
|
export const editorialChecksManifest = {
|
|
17
33
|
name: "editorial-checks",
|
|
18
34
|
contract: 1,
|
|
@@ -39,8 +55,13 @@ export const editorialChecksPlugin = {
|
|
|
39
55
|
const lambda = backend.editorialChecks.resources.lambda;
|
|
40
56
|
const stack = Stack.of(lambda);
|
|
41
57
|
lambda.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", context.graphqlEndpoint);
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
for (const name of EDITORIAL_CHECKS_SECRETS)
|
|
59
|
+
backend.editorialChecks.addEnvironment(name, secret(name));
|
|
60
|
+
lambda.addToRolePolicy(new PolicyStatement({
|
|
61
|
+
effect: Effect.ALLOW,
|
|
62
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
63
|
+
resources: editorialChecksSecretParameters(stack.region, stack.account, (process.env.AWS_APP_ID ?? "").trim()),
|
|
64
|
+
}));
|
|
44
65
|
lambda.addToRolePolicy(new PolicyStatement({
|
|
45
66
|
effect: Effect.ALLOW,
|
|
46
67
|
actions: ["appsync:GraphQL"],
|
|
@@ -14,6 +14,8 @@ export const EDITORIAL_CHECKS_MUTATIONS = ["importEditorialStarterPack", "recali
|
|
|
14
14
|
|
|
15
15
|
/** The model operations the function performs with its own role. */
|
|
16
16
|
export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
17
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
18
|
+
"Query/fields/getEditorialContext",
|
|
17
19
|
"Query/fields/listEditorialPolicies",
|
|
18
20
|
"Query/fields/getEditorialCheckSettings",
|
|
19
21
|
"Query/fields/listEditorialFeedbacks",
|
|
@@ -21,6 +23,22 @@ export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
|
21
23
|
"Mutation/fields/updateEditorialPolicy",
|
|
22
24
|
];
|
|
23
25
|
|
|
26
|
+
export const EDITORIAL_CHECKS_SECRETS = ["TYPESAFE_API_KEY", "OPENAI_API_KEY"];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* SSM parameters holding the provider keys. A Python function reads its secret()
|
|
30
|
+
* values itself (Amplify only resolves them for Node functions), so it needs read
|
|
31
|
+
* access to the app's branch and shared secret paths, and to the sandbox paths.
|
|
32
|
+
*/
|
|
33
|
+
export function editorialChecksSecretParameters(region: string, account: string, appId: string): string[] {
|
|
34
|
+
const arn = (path: string) => `arn:aws:ssm:${region}:${account}:parameter/amplify/${path}`;
|
|
35
|
+
return EDITORIAL_CHECKS_SECRETS.flatMap((name) => [
|
|
36
|
+
arn(`papyrus/*/${name}`),
|
|
37
|
+
arn(`shared/papyrus/${name}`),
|
|
38
|
+
...(appId ? [arn(`${appId}/*/${name}`), arn(`shared/${appId}/${name}`)] : []),
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
export const editorialChecksManifest: PluginManifest = {
|
|
25
43
|
name: "editorial-checks",
|
|
26
44
|
contract: 1,
|
|
@@ -48,8 +66,14 @@ export const editorialChecksPlugin: PapyrusPlugin<EditorialChecksPluginConfig, {
|
|
|
48
66
|
const lambda = backend.editorialChecks.resources.lambda as LambdaFunction;
|
|
49
67
|
const stack = Stack.of(lambda);
|
|
50
68
|
lambda.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", context.graphqlEndpoint);
|
|
51
|
-
backend.editorialChecks.addEnvironment(
|
|
52
|
-
|
|
69
|
+
for (const name of EDITORIAL_CHECKS_SECRETS) backend.editorialChecks.addEnvironment(name, secret(name));
|
|
70
|
+
lambda.addToRolePolicy(
|
|
71
|
+
new PolicyStatement({
|
|
72
|
+
effect: Effect.ALLOW,
|
|
73
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
74
|
+
resources: editorialChecksSecretParameters(stack.region, stack.account, (process.env.AWS_APP_ID ?? "").trim()),
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
53
77
|
lambda.addToRolePolicy(
|
|
54
78
|
new PolicyStatement({
|
|
55
79
|
effect: Effect.ALLOW,
|
package/amplify/site-backend.js
CHANGED
|
@@ -23,6 +23,7 @@ import { defineSiteData } from "./data/resource.js";
|
|
|
23
23
|
import { resolvePlugins } from "./plugins/registry.js";
|
|
24
24
|
import { wirePlugins } from "./plugins/wire.js";
|
|
25
25
|
import { assignmentAction } from "./functions/assignment-action/resource.js";
|
|
26
|
+
import { scopeAssignmentDataAccess } from "./functions/assignment-action/data-access.js";
|
|
26
27
|
import { categoryAction } from "./functions/category-action/resource.js";
|
|
27
28
|
import { ConsoleChatResponderStack } from "./functions/console-chat-responder/resource.js";
|
|
28
29
|
import { contentActions, setSiteFilesForContentActionsBundle } from "./functions/content-actions/resource.js";
|
|
@@ -304,10 +305,14 @@ export function defineSiteBackend(papyrusSite) {
|
|
|
304
305
|
}
|
|
305
306
|
grantCorporaRead(backend.knowledgeQuery.resources.lambda);
|
|
306
307
|
grantNewsroomReadWrite(backend.assignmentAction.resources.lambda);
|
|
308
|
+
scopeAssignmentDataAccess(backend.assignmentAction.resources.lambda, backend.data.resources.graphqlApi.arn);
|
|
307
309
|
grantNewsroomReadWrite(backend.categoryAction.resources.lambda);
|
|
308
310
|
grantNewsroomReadWrite(backend.newsroomSummary.resources.lambda);
|
|
309
311
|
const contentActionsLambda = backend.contentActions.resources.lambda;
|
|
310
312
|
grantNewsroomReadWriteDelete(contentActionsLambda);
|
|
313
|
+
// AppSync validates the token; pending intake additionally binds its actor
|
|
314
|
+
// and publication to this deployment's pool and existing API endpoint.
|
|
315
|
+
contentActionsLambda.addEnvironment("PAPYRUS_USER_POOL_ISSUER", `https://cognito-idp.${Stack.of(contentActionsLambda).region}.amazonaws.com/${backend.auth.resources.userPool.userPoolId}`);
|
|
311
316
|
contentActionsLambda.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl);
|
|
312
317
|
contentActionsLambda.addToRolePolicy(new PolicyStatement({
|
|
313
318
|
actions: ["appsync:GraphQL"],
|
package/amplify/site-backend.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { defineSiteData } from "./data/resource";
|
|
|
30
30
|
import { resolvePlugins } from "./plugins/registry";
|
|
31
31
|
import { wirePlugins } from "./plugins/wire";
|
|
32
32
|
import { assignmentAction } from "./functions/assignment-action/resource";
|
|
33
|
+
import { scopeAssignmentDataAccess } from "./functions/assignment-action/data-access";
|
|
33
34
|
import { categoryAction } from "./functions/category-action/resource";
|
|
34
35
|
import { ConsoleChatResponderStack } from "./functions/console-chat-responder/resource";
|
|
35
36
|
import { contentActions, setSiteFilesForContentActionsBundle } from "./functions/content-actions/resource";
|
|
@@ -387,10 +388,20 @@ export function defineSiteBackend(papyrusSite: PapyrusSite) {
|
|
|
387
388
|
}
|
|
388
389
|
grantCorporaRead(backend.knowledgeQuery.resources.lambda as LambdaFunction);
|
|
389
390
|
grantNewsroomReadWrite(backend.assignmentAction.resources.lambda as LambdaFunction);
|
|
391
|
+
scopeAssignmentDataAccess(
|
|
392
|
+
backend.assignmentAction.resources.lambda as LambdaFunction,
|
|
393
|
+
backend.data.resources.graphqlApi.arn,
|
|
394
|
+
);
|
|
390
395
|
grantNewsroomReadWrite(backend.categoryAction.resources.lambda as LambdaFunction);
|
|
391
396
|
grantNewsroomReadWrite(backend.newsroomSummary.resources.lambda as LambdaFunction);
|
|
392
397
|
const contentActionsLambda = backend.contentActions.resources.lambda as LambdaFunction;
|
|
393
398
|
grantNewsroomReadWriteDelete(contentActionsLambda);
|
|
399
|
+
// AppSync validates the token; pending intake additionally binds its actor
|
|
400
|
+
// and publication to this deployment's pool and existing API endpoint.
|
|
401
|
+
contentActionsLambda.addEnvironment(
|
|
402
|
+
"PAPYRUS_USER_POOL_ISSUER",
|
|
403
|
+
`https://cognito-idp.${Stack.of(contentActionsLambda).region}.amazonaws.com/${backend.auth.resources.userPool.userPoolId}`,
|
|
404
|
+
);
|
|
394
405
|
contentActionsLambda.addEnvironment(
|
|
395
406
|
"PAPYRUS_GRAPHQL_ENDPOINT",
|
|
396
407
|
backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl,
|
package/package.json
CHANGED
package/routes.manifest.json
CHANGED