@foir/sdk 0.21.0 → 0.23.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/CHANGELOG.md +135 -1
- package/README.md +15 -15
- package/dist/app/client.d.ts +93 -86
- package/dist/app/contract/proto/apps/v1/apps_pb.d.ts +14 -13
- package/dist/app/dispatch.d.ts +5 -5
- package/dist/app/errors.d.ts +17 -15
- package/dist/app/index.d.ts +4 -4
- package/dist/app/naming.d.ts +1 -1
- package/dist/app/ticket.d.ts +2 -2
- package/dist/app/vocabulary.generated.d.ts +2 -2
- package/dist/app-contract.js +3 -3
- package/dist/app.js +92 -90
- package/dist/auth/access.d.ts +19 -19
- package/dist/auth/auth.d.ts +251 -117
- package/dist/auth/grant-table.generated.d.ts +5 -4
- package/dist/auth/hosted.d.ts +38 -14
- package/dist/auth/index.d.ts +9 -9
- package/dist/auth/operations.d.ts +153 -33
- package/dist/auth/ops.d.ts +39 -21
- package/dist/auth/session-client.d.ts +3 -3
- package/dist/auth-session.js +3 -3
- package/dist/auth.js +400 -250
- package/dist/client.js +5 -8
- package/dist/codegen/preset.d.ts +1 -1
- package/dist/data/builtins.d.ts +26 -26
- package/dist/data/client.d.ts +3 -3
- package/dist/data/decimal.d.ts +1 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/manifest.d.ts +1 -1
- package/dist/data/schema.d.ts +2 -2
- package/dist/data/transport.d.ts +14 -13
- package/dist/data/ws-transport.d.ts +1 -1
- package/dist/data.js +8 -11
- package/dist/graphql/content-client.d.ts +13 -13
- package/dist/index.js +1 -4
- package/dist/live/drift.d.ts +1 -1
- package/dist/media/screen-capture.d.ts +1 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/session.d.ts +10 -10
- package/dist/next.js +78 -60
- package/dist/placement/index.d.ts +13 -13
- package/dist/placement.js +127 -116
- package/dist/push.js +62 -44
- package/dist/react/FoirFeedback.d.ts +2 -2
- package/dist/react/FoirProvider.d.ts +24 -24
- package/dist/react/SignIn.d.ts +4 -4
- package/dist/react/UserButton.d.ts +1 -1
- package/dist/react/feedback-notes.d.ts +8 -6
- package/dist/react/index.d.ts +3 -3
- package/dist/react.js +238 -209
- package/dist/realtime/index.d.ts +1 -1
- package/dist/server/callback-client.d.ts +4 -4
- package/dist/server/events-client.d.ts +21 -21
- package/dist/server/operation-types.d.ts +8 -8
- package/dist/server/require-scoped-token.d.ts +3 -3
- package/dist/server/types.d.ts +2 -2
- package/dist/server/verify-scoped-token.d.ts +3 -3
- package/dist/server.js +130 -126
- package/dist/transport/foir-http.d.ts +14 -14
- package/dist/transport/key-kind.d.ts +1 -12
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -1
package/dist/next.js
CHANGED
|
@@ -24,10 +24,7 @@ import { print as print2 } from "graphql";
|
|
|
24
24
|
var KEY_PREFIXES = [
|
|
25
25
|
["foir_pub_", "publishable"],
|
|
26
26
|
["foir_srv_", "server"],
|
|
27
|
-
["foir_adm_", "admin"]
|
|
28
|
-
["pk_", "publishable"],
|
|
29
|
-
["sk_", "older"],
|
|
30
|
-
["tc_", "older"]
|
|
27
|
+
["foir_adm_", "admin"]
|
|
31
28
|
];
|
|
32
29
|
function foirKeyKind(apiKey) {
|
|
33
30
|
if (typeof apiKey !== "string") return void 0;
|
|
@@ -211,8 +208,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
211
208
|
downgrade;
|
|
212
209
|
constructor(downgrade) {
|
|
213
210
|
super(
|
|
214
|
-
`[foir] your
|
|
215
|
-
{ graphQLErrors: [{ message: "
|
|
211
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
|
|
212
|
+
{ graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
|
|
216
213
|
);
|
|
217
214
|
this.name = "FoirAuthDowngradedError";
|
|
218
215
|
this.downgrade = downgrade;
|
|
@@ -220,7 +217,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
220
217
|
};
|
|
221
218
|
var FoirSessionExpiredError = class extends FoirRequestError {
|
|
222
219
|
constructor(options) {
|
|
223
|
-
super("[foir] the
|
|
220
|
+
super("[foir] the user session has expired and could not be refreshed: sign in again", {
|
|
224
221
|
cause: options?.cause,
|
|
225
222
|
graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
|
|
226
223
|
});
|
|
@@ -237,7 +234,7 @@ function reportAuthDowngrade(envelope) {
|
|
|
237
234
|
if (warnedDowngrade === downgrade.reason) return;
|
|
238
235
|
warnedDowngrade = downgrade.reason;
|
|
239
236
|
console.warn(
|
|
240
|
-
`[foir] your
|
|
237
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}) \u2014 this request was served as ${downgrade.servedAs}, so auth-targeted content resolved to its anonymous variant. ` + (downgrade.reason === "expired" ? "Re-exchange the token and retry." : "Check which credential you are sending.")
|
|
241
238
|
);
|
|
242
239
|
}
|
|
243
240
|
function refusePrunedDocument(json, options) {
|
|
@@ -956,42 +953,63 @@ var AUTH_OPS = {
|
|
|
956
953
|
authConfig: "authConfig",
|
|
957
954
|
authProviders: "authProviders",
|
|
958
955
|
defaultAuthProvider: "defaultAuthProvider",
|
|
959
|
-
|
|
956
|
+
signupContext: "signupContext",
|
|
960
957
|
// Post-credential self reads.
|
|
961
958
|
currentUser: "currentUser",
|
|
962
959
|
myProviderToken: "myProviderToken",
|
|
963
|
-
// What the calling credential may do, and for a signed-in
|
|
960
|
+
// What the calling credential may do, and for a signed-in user the roles
|
|
964
961
|
// they hold. `./access` reads it.
|
|
965
962
|
whoami: "whoami",
|
|
966
963
|
// Methods.
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
964
|
+
login: "login",
|
|
965
|
+
register: "register",
|
|
966
|
+
requestOTP: "requestOTP",
|
|
967
|
+
loginWithOTP: "loginWithOTP",
|
|
971
968
|
// Session lifecycle.
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
// Password management and account lifecycle
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
969
|
+
refreshToken: "refreshToken",
|
|
970
|
+
logout: "logout",
|
|
971
|
+
joinWaitlist: "joinWaitlist",
|
|
972
|
+
// Password management and account lifecycle.
|
|
973
|
+
requestPasswordReset: "requestPasswordReset",
|
|
974
|
+
resetPassword: "resetPassword",
|
|
975
|
+
updatePassword: "updatePassword",
|
|
976
|
+
verifyEmail: "verifyEmail",
|
|
980
977
|
deleteMyAccount: "deleteMyAccount",
|
|
981
|
-
// The
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
//
|
|
986
|
-
//
|
|
978
|
+
// The user's own profile, and the apps they have agreed may act for them.
|
|
979
|
+
updateMyProfile: "updateMyProfile",
|
|
980
|
+
myAuthorizedApps: "myAuthorizedApps",
|
|
981
|
+
revokeAuthorizedApp: "revokeAuthorizedApp",
|
|
982
|
+
// Where they are signed in, their personal access tokens, and the
|
|
983
|
+
// providers they sign in with.
|
|
984
|
+
mySessions: "mySessions",
|
|
985
|
+
endMySession: "endMySession",
|
|
986
|
+
signOutEverywhere: "signOutEverywhere",
|
|
987
|
+
createPersonalAccessToken: "createPersonalAccessToken",
|
|
988
|
+
mySignInMethods: "mySignInMethods",
|
|
989
|
+
connectSignInMethod: "connectSignInMethod",
|
|
990
|
+
disconnectSignInMethod: "disconnectSignInMethod",
|
|
991
|
+
// Their second factors. A change needs one proven first, by a step-up.
|
|
992
|
+
myFactors: "myFactors",
|
|
993
|
+
beginStepUp: "beginStepUp",
|
|
994
|
+
completeStepUp: "completeStepUp",
|
|
995
|
+
beginFactorEnrollment: "beginFactorEnrollment",
|
|
996
|
+
completeFactorEnrollment: "completeFactorEnrollment",
|
|
997
|
+
removeFactor: "removeFactor",
|
|
998
|
+
makeRecoveryCodes: "makeRecoveryCodes",
|
|
999
|
+
// The organizations the caller may stand in. Nothing selects one: a request
|
|
1000
|
+
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
1001
|
+
// platform checks the caller stands there every time.
|
|
987
1002
|
myOrganizations: "myOrganizations",
|
|
988
|
-
selectMyOrganization: "selectMyOrganization",
|
|
989
1003
|
// Founding one. Present in a manifest only when the project opted in.
|
|
990
|
-
|
|
1004
|
+
registerOrganization: "registerOrganization",
|
|
991
1005
|
// External identity providers.
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1006
|
+
loginWithProvider: "loginWithProvider",
|
|
1007
|
+
providerCallback: "providerCallback",
|
|
1008
|
+
providerVerifyOTP: "providerVerifyOTP",
|
|
1009
|
+
// A provider sign-in that names an address somebody already holds, waiting
|
|
1010
|
+
// for the code sent to it.
|
|
1011
|
+
confirmProviderLink: "confirmProviderLink",
|
|
1012
|
+
resendProviderLinkCode: "resendProviderLinkCode"
|
|
995
1013
|
};
|
|
996
1014
|
function authClientConfig(env, options) {
|
|
997
1015
|
const apiKey = resolveFoirApiKey(env);
|
|
@@ -1054,58 +1072,58 @@ function stripUndefined(args) {
|
|
|
1054
1072
|
// src/auth/auth.ts
|
|
1055
1073
|
var TOKEN_KEY = "foir:token";
|
|
1056
1074
|
var REFRESH_KEY = "foir:refreshToken";
|
|
1057
|
-
var
|
|
1075
|
+
var USER_ID_KEY = "foir:userId";
|
|
1058
1076
|
var EMAIL_KEY = "foir:email";
|
|
1059
|
-
async function
|
|
1060
|
-
return authOp(env, AUTH_OPS.
|
|
1077
|
+
async function login(env, vars) {
|
|
1078
|
+
return authOp(env, AUTH_OPS.login, vars);
|
|
1061
1079
|
}
|
|
1062
|
-
async function
|
|
1063
|
-
return authOp(env, AUTH_OPS.
|
|
1080
|
+
async function register(env, vars) {
|
|
1081
|
+
return authOp(env, AUTH_OPS.register, vars);
|
|
1064
1082
|
}
|
|
1065
|
-
async function
|
|
1066
|
-
return authOp(env, AUTH_OPS.
|
|
1083
|
+
async function loginWithOTP(env, vars) {
|
|
1084
|
+
return authOp(env, AUTH_OPS.loginWithOTP, vars);
|
|
1067
1085
|
}
|
|
1068
|
-
async function
|
|
1069
|
-
return authOp(env, AUTH_OPS.
|
|
1086
|
+
async function providerCallback(env, input) {
|
|
1087
|
+
return authOp(env, AUTH_OPS.providerCallback, { input });
|
|
1070
1088
|
}
|
|
1071
1089
|
async function saveSession(adapter, result) {
|
|
1072
1090
|
await adapter.set(TOKEN_KEY, result.token);
|
|
1073
1091
|
await adapter.set(REFRESH_KEY, result.refreshToken);
|
|
1074
|
-
if (!result.
|
|
1075
|
-
return await
|
|
1092
|
+
if (!result.userId) {
|
|
1093
|
+
return await getSessionUser(adapter) ?? { id: "", email: "" };
|
|
1076
1094
|
}
|
|
1077
|
-
await adapter.set(
|
|
1095
|
+
await adapter.set(USER_ID_KEY, result.userId);
|
|
1078
1096
|
await adapter.set(EMAIL_KEY, result.email);
|
|
1079
|
-
return { id: result.
|
|
1097
|
+
return { id: result.userId, email: result.email };
|
|
1080
1098
|
}
|
|
1081
1099
|
async function clearSession(adapter) {
|
|
1082
1100
|
await adapter.unset(TOKEN_KEY);
|
|
1083
1101
|
await adapter.unset(REFRESH_KEY);
|
|
1084
|
-
await adapter.unset(
|
|
1102
|
+
await adapter.unset(USER_ID_KEY);
|
|
1085
1103
|
await adapter.unset(EMAIL_KEY);
|
|
1086
1104
|
}
|
|
1087
1105
|
async function getSessionToken(adapter) {
|
|
1088
1106
|
return await adapter.get(TOKEN_KEY) ?? null;
|
|
1089
1107
|
}
|
|
1090
|
-
async function
|
|
1091
|
-
const id = await adapter.get(
|
|
1108
|
+
async function getSessionUser(adapter) {
|
|
1109
|
+
const id = await adapter.get(USER_ID_KEY) ?? null;
|
|
1092
1110
|
const email = await adapter.get(EMAIL_KEY) ?? null;
|
|
1093
1111
|
if (!id) return null;
|
|
1094
1112
|
return { id, email: email ?? "" };
|
|
1095
1113
|
}
|
|
1096
|
-
async function
|
|
1097
|
-
return saveSession(adapter, await
|
|
1114
|
+
async function signInWithPassword(adapter, env, vars) {
|
|
1115
|
+
return saveSession(adapter, await login(env, vars));
|
|
1098
1116
|
}
|
|
1099
|
-
async function
|
|
1100
|
-
return saveSession(adapter, await
|
|
1117
|
+
async function signUpWithPassword(adapter, env, vars) {
|
|
1118
|
+
return saveSession(adapter, await register(env, vars));
|
|
1101
1119
|
}
|
|
1102
|
-
async function
|
|
1103
|
-
return saveSession(adapter, await
|
|
1120
|
+
async function signInWithOTP(adapter, env, vars) {
|
|
1121
|
+
return saveSession(adapter, await loginWithOTP(env, vars));
|
|
1104
1122
|
}
|
|
1105
1123
|
|
|
1106
1124
|
// src/next/session.ts
|
|
1107
1125
|
async function getFoirSession(store) {
|
|
1108
|
-
return
|
|
1126
|
+
return getSessionUser(createFoirCookieAdapter(store));
|
|
1109
1127
|
}
|
|
1110
1128
|
async function getFoirToken(store) {
|
|
1111
1129
|
return getSessionToken(createFoirCookieAdapter(store));
|
|
@@ -1120,7 +1138,7 @@ function foirSignInWithPassword({
|
|
|
1120
1138
|
password,
|
|
1121
1139
|
cookieOptions
|
|
1122
1140
|
}) {
|
|
1123
|
-
return
|
|
1141
|
+
return signInWithPassword(createFoirCookieAdapter(store, { cookieOptions }), env, {
|
|
1124
1142
|
email,
|
|
1125
1143
|
password
|
|
1126
1144
|
});
|
|
@@ -1132,7 +1150,7 @@ function foirRegisterWithPassword({
|
|
|
1132
1150
|
password,
|
|
1133
1151
|
cookieOptions
|
|
1134
1152
|
}) {
|
|
1135
|
-
return
|
|
1153
|
+
return signUpWithPassword(createFoirCookieAdapter(store, { cookieOptions }), env, {
|
|
1136
1154
|
email,
|
|
1137
1155
|
password
|
|
1138
1156
|
});
|
|
@@ -1144,13 +1162,13 @@ function foirSignInWithOtp({
|
|
|
1144
1162
|
otp,
|
|
1145
1163
|
cookieOptions
|
|
1146
1164
|
}) {
|
|
1147
|
-
return
|
|
1165
|
+
return signInWithOTP(createFoirCookieAdapter(store, { cookieOptions }), env, {
|
|
1148
1166
|
email,
|
|
1149
1167
|
otp
|
|
1150
1168
|
});
|
|
1151
1169
|
}
|
|
1152
1170
|
async function completeFoirProviderLogin(opts) {
|
|
1153
|
-
const result = await
|
|
1171
|
+
const result = await providerCallback(opts.env, {
|
|
1154
1172
|
providerKey: opts.providerKey,
|
|
1155
1173
|
code: opts.code,
|
|
1156
1174
|
state: opts.state
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@foir/sdk/placement`: your app's own
|
|
2
|
+
* `@foir/sdk/placement`: your app's own console, shown inside Foir's.
|
|
3
3
|
*
|
|
4
4
|
* A full-page placement is loaded in a frame on the Foir console. No cookie
|
|
5
5
|
* reaches a cross-site frame, so the console hands the page a short-lived token
|
|
6
6
|
* instead, by message, to your placement's origin only. The token is the
|
|
7
|
-
*
|
|
8
|
-
* is installed at, carrying what your placement declares and this
|
|
7
|
+
* person looking at the page, acting through your app at the level your app
|
|
8
|
+
* is installed at, carrying what your placement declares and this person
|
|
9
9
|
* holds, as grants in the permission catalogue's names. Your page runs no
|
|
10
10
|
* login of its own.
|
|
11
11
|
*
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*
|
|
23
23
|
* The console also tells the page how it looks, so the page can match it: pass
|
|
24
|
-
* `onAppearance` and apply the theme it names.
|
|
24
|
+
* `onAppearance` and apply the theme it names. Their console theme is
|
|
25
25
|
* their own setting and need not be their system's.
|
|
26
26
|
*
|
|
27
27
|
* The browser shows the page blank, however well it is authenticated, unless
|
|
@@ -40,7 +40,7 @@ export declare const PLACEMENT_MESSAGE: {
|
|
|
40
40
|
readonly refresh: "foir:placement:refresh";
|
|
41
41
|
/** Console to page: no token, and why. */
|
|
42
42
|
readonly refused: "foir:placement:refused";
|
|
43
|
-
/** Console to page: the
|
|
43
|
+
/** Console to page: the person signed out. */
|
|
44
44
|
readonly signedOut: "foir:placement:signed-out";
|
|
45
45
|
/** Console to page: how the console looks, now and whenever it changes. */
|
|
46
46
|
readonly appearance: "foir:placement:appearance";
|
|
@@ -55,7 +55,7 @@ export interface PlacementGrant {
|
|
|
55
55
|
orgId: string;
|
|
56
56
|
/**
|
|
57
57
|
* What the token carries: the grants your placement declares that this
|
|
58
|
-
*
|
|
58
|
+
* person holds, in the permission catalogue's names. A grant the person
|
|
59
59
|
* holds part of is carried as that part, so ask with `can` and not by
|
|
60
60
|
* looking for a spelling here.
|
|
61
61
|
*/
|
|
@@ -71,7 +71,7 @@ type PlacementGrantMessage = Pick<PlacementGrant, 'accessToken' | 'expiresAt' |
|
|
|
71
71
|
/** How the console looks, for an embedded page to match. */
|
|
72
72
|
export interface PlacementAppearance {
|
|
73
73
|
/**
|
|
74
|
-
* The console's theme as the
|
|
74
|
+
* The console's theme as the person sees it: their own choice, or their
|
|
75
75
|
* system's when they left it to the system.
|
|
76
76
|
*/
|
|
77
77
|
theme: 'light' | 'dark';
|
|
@@ -97,7 +97,7 @@ export type PlacementPageMessage = {
|
|
|
97
97
|
};
|
|
98
98
|
/**
|
|
99
99
|
* The Foir consoles, per environment. A project that serves the console on its
|
|
100
|
-
* own verified
|
|
100
|
+
* own verified console domain adds that origin too: the install preview lists it.
|
|
101
101
|
*/
|
|
102
102
|
export declare const FOIR_CONSOLE_ORIGINS: {
|
|
103
103
|
readonly production: readonly ["https://app.foir.io"];
|
|
@@ -127,7 +127,7 @@ export interface ConnectPlacementOptions {
|
|
|
127
127
|
refreshBeforeMs?: number;
|
|
128
128
|
/**
|
|
129
129
|
* Called with how the console looks: once as soon as the console says, which
|
|
130
|
-
* is before the token arrives, and again whenever the
|
|
130
|
+
* is before the token arrives, and again whenever the person changes it.
|
|
131
131
|
* Apply it instead of the system's preference, so the page matches the
|
|
132
132
|
* console it is shown in.
|
|
133
133
|
*/
|
|
@@ -143,22 +143,22 @@ export interface PlacementSession {
|
|
|
143
143
|
readonly grant: PlacementGrant;
|
|
144
144
|
/**
|
|
145
145
|
* How the console looks now, or null when the console has not said (a console
|
|
146
|
-
* older than this). Kept current as the
|
|
146
|
+
* older than this). Kept current as the person changes it.
|
|
147
147
|
*/
|
|
148
148
|
readonly appearance: PlacementAppearance | null;
|
|
149
149
|
/**
|
|
150
|
-
* Whether the token covers this grant, to hide or grey out what the
|
|
150
|
+
* Whether the token covers this grant, to hide or grey out what the person
|
|
151
151
|
* cannot do. It answers as Foir's API would: a grant for every model covers
|
|
152
152
|
* the same grant for one, and one for every owner's rows covers a person's
|
|
153
153
|
* own. `can('records:read.all:order')` is true for a token that carries
|
|
154
154
|
* `records:read.all`.
|
|
155
155
|
*/
|
|
156
156
|
can(grant: string): boolean;
|
|
157
|
-
/** A GraphQL request to Foir's API as the
|
|
157
|
+
/** A GraphQL request to Foir's API as the person, through your app. */
|
|
158
158
|
request<TResult = unknown>(document: string | {
|
|
159
159
|
toString(): string;
|
|
160
160
|
}, variables?: Record<string, unknown>): Promise<TResult>;
|
|
161
|
-
/** Called when the
|
|
161
|
+
/** Called when the person signs out; the session holds no token from then on. */
|
|
162
162
|
onSignedOut(listener: () => void): () => void;
|
|
163
163
|
/** Stop listening and refreshing. */
|
|
164
164
|
close(): void;
|
package/dist/placement.js
CHANGED
|
@@ -1,121 +1,124 @@
|
|
|
1
1
|
// src/auth/grant-table.generated.ts
|
|
2
2
|
var GRANT_TABLE = [
|
|
3
|
-
["records:read",
|
|
4
|
-
["records:create",
|
|
5
|
-
["records:write",
|
|
6
|
-
["records:delete",
|
|
7
|
-
["records:publish",
|
|
8
|
-
["records:attribute",
|
|
9
|
-
["search:read",
|
|
10
|
-
["embeddings:read",
|
|
11
|
-
["embeddings:write",
|
|
12
|
-
["schemas:read",
|
|
13
|
-
["drafts:read",
|
|
14
|
-
["files:read",
|
|
15
|
-
["files:write",
|
|
16
|
-
["files:delete",
|
|
17
|
-
["notifications:read",
|
|
18
|
-
["notifications:write",
|
|
19
|
-
["notifications:consent",
|
|
20
|
-
["pushcredentials:read",
|
|
21
|
-
["pushcredentials:write",
|
|
22
|
-
["notes:read",
|
|
23
|
-
["notes:write",
|
|
24
|
-
["notes:create",
|
|
25
|
-
["approvals:read",
|
|
26
|
-
["approvals:write",
|
|
27
|
-
["rooms:read",
|
|
28
|
-
["rooms:write",
|
|
29
|
-
["presence:read",
|
|
30
|
-
["operations:read",
|
|
31
|
-
["operations:execute",
|
|
32
|
-
["operations:write",
|
|
33
|
-
["operations:complete",
|
|
34
|
-
["status:write",
|
|
35
|
-
["appconfig:read",
|
|
36
|
-
["schedules:read",
|
|
37
|
-
["schedules:write",
|
|
38
|
-
["hooks:read",
|
|
39
|
-
["hooks:write",
|
|
40
|
-
["
|
|
41
|
-
["
|
|
42
|
-
["domains:read",
|
|
43
|
-
["domains:write",
|
|
44
|
-
["secrets:read",
|
|
45
|
-
["secrets:list",
|
|
46
|
-
["secrets:put",
|
|
47
|
-
["secrets:delete",
|
|
48
|
-
["appsecrets:read",
|
|
49
|
-
["appsecrets:list",
|
|
50
|
-
["appsecrets:put",
|
|
51
|
-
["appsecrets:delete",
|
|
52
|
-
["opsecrets:read",
|
|
53
|
-
["opsecrets:write",
|
|
54
|
-
["settings:read",
|
|
55
|
-
["settings:write",
|
|
56
|
-
["designtokens:read",
|
|
57
|
-
["designtokens:write",
|
|
58
|
-
["models:read",
|
|
59
|
-
["models:write",
|
|
60
|
-
["configs:read",
|
|
61
|
-
["configs:write",
|
|
62
|
-
["apps:read",
|
|
63
|
-
["apps:write",
|
|
64
|
-
["events:read",
|
|
65
|
-
["targeting:debug",
|
|
66
|
-
["visitors:forward",
|
|
67
|
-
["apikeys:read",
|
|
68
|
-
["apikeys:write",
|
|
69
|
-
["organizations:read",
|
|
70
|
-
["organizations:write",
|
|
71
|
-
["organizations:create",
|
|
72
|
-
["organizations:delete",
|
|
73
|
-
["organizations:reach",
|
|
74
|
-
["members:read",
|
|
75
|
-
["members:write",
|
|
76
|
-
["roles:read",
|
|
77
|
-
["roles:write",
|
|
78
|
-
["projects:read",
|
|
79
|
-
["projects:write",
|
|
80
|
-
["project:
|
|
81
|
-
["
|
|
82
|
-
["projects:
|
|
83
|
-
["
|
|
84
|
-
["tenants:
|
|
85
|
-
["tenants:
|
|
86
|
-
["
|
|
87
|
-
["
|
|
88
|
-
["
|
|
89
|
-
["billing:
|
|
90
|
-
["
|
|
91
|
-
["
|
|
92
|
-
["controlkeys:
|
|
93
|
-
["
|
|
94
|
-
["tenanthooks:
|
|
95
|
-
["
|
|
96
|
-
["
|
|
97
|
-
["
|
|
98
|
-
["platform:
|
|
99
|
-
["platform:
|
|
100
|
-
["
|
|
101
|
-
["users:
|
|
102
|
-
["users:
|
|
103
|
-
["users:
|
|
104
|
-
["users:
|
|
105
|
-
["
|
|
106
|
-
["identities:
|
|
107
|
-
["
|
|
108
|
-
["usersecrets:
|
|
109
|
-
["usersecrets:
|
|
110
|
-
["usersecrets:
|
|
111
|
-
["
|
|
112
|
-
["
|
|
113
|
-
["signinproviders:
|
|
3
|
+
["records:read", "model", ["own", "all"], []],
|
|
4
|
+
["records:create", "model", [], []],
|
|
5
|
+
["records:write", "model", ["own", "all"], []],
|
|
6
|
+
["records:delete", "model", ["own", "all"], []],
|
|
7
|
+
["records:publish", "model", ["own", "all"], []],
|
|
8
|
+
["records:attribute", "model", [], []],
|
|
9
|
+
["search:read", "model", [], []],
|
|
10
|
+
["embeddings:read", "none", [], []],
|
|
11
|
+
["embeddings:write", "none", [], []],
|
|
12
|
+
["schemas:read", "none", [], []],
|
|
13
|
+
["drafts:read", "none", [], []],
|
|
14
|
+
["files:read", "none", ["own", "all"], []],
|
|
15
|
+
["files:write", "none", ["own", "all"], []],
|
|
16
|
+
["files:delete", "none", ["own", "all"], []],
|
|
17
|
+
["notifications:read", "none", ["own", "all"], []],
|
|
18
|
+
["notifications:write", "none", ["own", "named", "all"], []],
|
|
19
|
+
["notifications:consent", "none", [], []],
|
|
20
|
+
["pushcredentials:read", "none", [], []],
|
|
21
|
+
["pushcredentials:write", "none", [], []],
|
|
22
|
+
["notes:read", "none", ["own", "all"], []],
|
|
23
|
+
["notes:write", "none", ["own", "all"], []],
|
|
24
|
+
["notes:create", "none", [], []],
|
|
25
|
+
["approvals:read", "none", [], []],
|
|
26
|
+
["approvals:write", "none", [], []],
|
|
27
|
+
["rooms:read", "model", [], []],
|
|
28
|
+
["rooms:write", "model", [], []],
|
|
29
|
+
["presence:read", "model", [], []],
|
|
30
|
+
["operations:read", "none", [], []],
|
|
31
|
+
["operations:execute", "operation", [], []],
|
|
32
|
+
["operations:write", "none", [], []],
|
|
33
|
+
["operations:complete", "none", [], []],
|
|
34
|
+
["status:write", "none", [], []],
|
|
35
|
+
["appconfig:read", "none", [], []],
|
|
36
|
+
["schedules:read", "none", [], []],
|
|
37
|
+
["schedules:write", "none", [], []],
|
|
38
|
+
["hooks:read", "none", [], []],
|
|
39
|
+
["hooks:write", "none", [], []],
|
|
40
|
+
["authclients:read", "none", [], []],
|
|
41
|
+
["authclients:write", "none", [], []],
|
|
42
|
+
["domains:read", "none", [], []],
|
|
43
|
+
["domains:write", "none", [], []],
|
|
44
|
+
["secrets:read", "none", [], []],
|
|
45
|
+
["secrets:list", "none", [], []],
|
|
46
|
+
["secrets:put", "none", [], []],
|
|
47
|
+
["secrets:delete", "none", [], []],
|
|
48
|
+
["appsecrets:read", "none", [], []],
|
|
49
|
+
["appsecrets:list", "none", [], []],
|
|
50
|
+
["appsecrets:put", "none", [], []],
|
|
51
|
+
["appsecrets:delete", "none", [], []],
|
|
52
|
+
["opsecrets:read", "none", [], []],
|
|
53
|
+
["opsecrets:write", "none", [], []],
|
|
54
|
+
["settings:read", "none", [], []],
|
|
55
|
+
["settings:write", "none", [], []],
|
|
56
|
+
["designtokens:read", "none", [], []],
|
|
57
|
+
["designtokens:write", "none", [], []],
|
|
58
|
+
["models:read", "none", [], []],
|
|
59
|
+
["models:write", "none", [], []],
|
|
60
|
+
["configs:read", "none", [], []],
|
|
61
|
+
["configs:write", "none", [], []],
|
|
62
|
+
["apps:read", "none", [], []],
|
|
63
|
+
["apps:write", "none", [], []],
|
|
64
|
+
["events:read", "none", [], []],
|
|
65
|
+
["targeting:debug", "none", [], []],
|
|
66
|
+
["visitors:forward", "none", [], []],
|
|
67
|
+
["apikeys:read", "none", [], []],
|
|
68
|
+
["apikeys:write", "none", [], []],
|
|
69
|
+
["organizations:read", "none", [], []],
|
|
70
|
+
["organizations:write", "none", [], []],
|
|
71
|
+
["organizations:create", "none", [], []],
|
|
72
|
+
["organizations:delete", "none", [], []],
|
|
73
|
+
["organizations:reach", "none", [], []],
|
|
74
|
+
["members:read", "none", [], []],
|
|
75
|
+
["members:write", "none", [], []],
|
|
76
|
+
["roles:read", "none", [], []],
|
|
77
|
+
["roles:write", "none", [], []],
|
|
78
|
+
["projects:read", "none", [], []],
|
|
79
|
+
["projects:write", "none", [], []],
|
|
80
|
+
["project:stand", "none", [], []],
|
|
81
|
+
["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "authclients:read", "authclients:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
|
|
82
|
+
["projects:create", "none", [], []],
|
|
83
|
+
["projects:delete", "none", [], []],
|
|
84
|
+
["tenants:read", "none", [], []],
|
|
85
|
+
["tenants:write", "none", [], []],
|
|
86
|
+
["tenants:delete", "none", [], []],
|
|
87
|
+
["audit:read", "none", [], []],
|
|
88
|
+
["export:run", "none", [], []],
|
|
89
|
+
["billing:read", "none", [], []],
|
|
90
|
+
["billing:manage", "none", [], []],
|
|
91
|
+
["usage:read", "none", [], []],
|
|
92
|
+
["controlkeys:read", "none", [], []],
|
|
93
|
+
["controlkeys:write", "none", [], []],
|
|
94
|
+
["tenanthooks:read", "none", [], []],
|
|
95
|
+
["tenanthooks:write", "none", [], []],
|
|
96
|
+
["apps:install", "none", [], []],
|
|
97
|
+
["tenant:manage", "none", [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "export:run", "billing:read", "billing:manage", "usage:read", "controlkeys:read", "controlkeys:write", "tenanthooks:read", "tenanthooks:write", "apps:install"]],
|
|
98
|
+
["platform:read", "none", [], []],
|
|
99
|
+
["platform:manage", "none", [], ["tenant:manage", "platform:read", "platform:create_workspace"]],
|
|
100
|
+
["platform:create_workspace", "none", [], []],
|
|
101
|
+
["users:read", "none", ["own", "named", "all"], []],
|
|
102
|
+
["users:write", "none", ["own", "all"], []],
|
|
103
|
+
["users:setup", "none", [], []],
|
|
104
|
+
["users:delete", "none", ["own", "all"], []],
|
|
105
|
+
["users:create", "none", [], []],
|
|
106
|
+
["identities:read", "none", [], []],
|
|
107
|
+
["identities:delete", "none", [], []],
|
|
108
|
+
["usersecrets:read", "none", [], []],
|
|
109
|
+
["usersecrets:list", "none", [], []],
|
|
110
|
+
["usersecrets:put", "none", [], []],
|
|
111
|
+
["usersecrets:delete", "none", [], []],
|
|
112
|
+
["auth:login_with_foir", "none", [], []],
|
|
113
|
+
["signinproviders:read", "none", [], []],
|
|
114
|
+
["signinproviders:write", "none", [], []]
|
|
114
115
|
];
|
|
115
116
|
|
|
116
117
|
// src/auth/grants.ts
|
|
117
118
|
var WILDCARD = "*";
|
|
118
119
|
var RESOURCE_WILDCARD = ":*";
|
|
120
|
+
var FIELD_PERMISSIONS = ["records:read", "records:write"];
|
|
121
|
+
var FIELD_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
119
122
|
var index;
|
|
120
123
|
function catalogue() {
|
|
121
124
|
if (!index) {
|
|
@@ -156,15 +159,23 @@ function parseGrant(s) {
|
|
|
156
159
|
let target = "";
|
|
157
160
|
if (rest.startsWith(":")) {
|
|
158
161
|
target = rest.slice(1);
|
|
159
|
-
if (
|
|
162
|
+
if (permission[1] === "none") return null;
|
|
160
163
|
if (target === "" || /[: \t\n*]/.test(target)) return null;
|
|
164
|
+
const dot = target.indexOf(".");
|
|
165
|
+
if (permission[1] === "model" && dot >= 0) {
|
|
166
|
+
if (!FIELD_PERMISSIONS.includes(name) || dot === 0 || !FIELD_KEY.test(target.slice(dot + 1))) return null;
|
|
167
|
+
}
|
|
161
168
|
}
|
|
162
169
|
return { permission, reach, target };
|
|
163
170
|
}
|
|
164
171
|
return null;
|
|
165
172
|
}
|
|
166
173
|
function covers(held, required) {
|
|
167
|
-
|
|
174
|
+
if (held.permission !== required.permission || reachRank[held.reach] < reachRank[required.reach]) return false;
|
|
175
|
+
if (required.permission[1] === "model" && required.target.includes(".")) {
|
|
176
|
+
return held.target === required.target || held.target === "" && held.reach === "all";
|
|
177
|
+
}
|
|
178
|
+
return held.target === "" || held.target === required.target;
|
|
168
179
|
}
|
|
169
180
|
function resourceWildcard(held) {
|
|
170
181
|
if (!held.endsWith(RESOURCE_WILDCARD)) return null;
|
|
@@ -202,7 +213,7 @@ var PLACEMENT_MESSAGE = {
|
|
|
202
213
|
refresh: "foir:placement:refresh",
|
|
203
214
|
/** Console to page: no token, and why. */
|
|
204
215
|
refused: "foir:placement:refused",
|
|
205
|
-
/** Console to page: the
|
|
216
|
+
/** Console to page: the person signed out. */
|
|
206
217
|
signedOut: "foir:placement:signed-out",
|
|
207
218
|
/** Console to page: how the console looks, now and whenever it changes. */
|
|
208
219
|
appearance: "foir:placement:appearance"
|
|
@@ -296,7 +307,7 @@ function connectPlacement(options) {
|
|
|
296
307
|
};
|
|
297
308
|
const session = {
|
|
298
309
|
get grant() {
|
|
299
|
-
if (!grant) throw new PlacementRefused("the
|
|
310
|
+
if (!grant) throw new PlacementRefused("the person using the console has signed out");
|
|
300
311
|
return grant;
|
|
301
312
|
},
|
|
302
313
|
get appearance() {
|
|
@@ -306,7 +317,7 @@ function connectPlacement(options) {
|
|
|
306
317
|
// names a placement declared then; and by what the list covers.
|
|
307
318
|
can: (asked) => grant ? grant.grants.includes(asked) || holds(grant.grants, asked) : false,
|
|
308
319
|
async request(document, variables) {
|
|
309
|
-
if (!grant) throw new PlacementRefused("the
|
|
320
|
+
if (!grant) throw new PlacementRefused("the person using the console has signed out");
|
|
310
321
|
const res = await doFetch(`${apiUrl}/graphql`, {
|
|
311
322
|
method: "POST",
|
|
312
323
|
headers: { "content-type": "application/json", authorization: `Bearer ${grant.accessToken}` },
|