@firecms/user_management 3.0.0-canary.144 → 3.0.0-canary.146
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/dist/hooks/useBuildUserManagement.d.ts +5 -3
- package/dist/index.es.js +41 -25
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +41 -25
- package/dist/index.umd.js.map +1 -1
- package/dist/types/user_management.d.ts +1 -5
- package/dist/useUserManagementPlugin.d.ts +5 -5
- package/dist/utils/permissions.d.ts +2 -2
- package/package.json +5 -5
- package/src/components/roles/RolesDetailsForm.tsx +1 -0
- package/src/components/users/UsersTable.tsx +0 -2
- package/src/hooks/useBuildUserManagement.tsx +89 -43
- package/src/types/user_management.tsx +2 -6
- package/src/useUserManagementPlugin.tsx +6 -6
- package/src/utils/permissions.ts +2 -2
@@ -1,10 +1,10 @@
|
|
1
|
-
import { FireCMSPlugin } from "@firecms/core";
|
2
|
-
import {
|
3
|
-
export declare function useUserManagementPlugin({ userManagement }: {
|
4
|
-
userManagement: UserManagement
|
1
|
+
import { FireCMSPlugin, User } from "@firecms/core";
|
2
|
+
import { UserManagement } from "./types";
|
3
|
+
export declare function useUserManagementPlugin<USER extends User = any>({ userManagement }: {
|
4
|
+
userManagement: UserManagement<USER>;
|
5
5
|
}): FireCMSPlugin;
|
6
6
|
export declare function IntroWidget({ noUsers, noRoles, userManagement }: {
|
7
7
|
noUsers: boolean;
|
8
8
|
noRoles: boolean;
|
9
|
-
userManagement: UserManagement<
|
9
|
+
userManagement: UserManagement<any>;
|
10
10
|
}): import("react/jsx-runtime").JSX.Element;
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { EntityCollection, Permissions, Role, User } from "@firecms/core";
|
2
2
|
export declare const RESERVED_GROUPS: string[];
|
3
|
-
export declare function resolveUserRolePermissions<
|
3
|
+
export declare function resolveUserRolePermissions<USER extends User>({ collection, user }: {
|
4
4
|
collection: EntityCollection<any>;
|
5
|
-
user:
|
5
|
+
user: USER | null;
|
6
6
|
}): Permissions;
|
7
7
|
export declare function getUserRoles(roles: Role[], fireCMSUser: User): Role[] | undefined;
|
8
8
|
export declare const areRolesEqual: (rolesA: Role[], rolesB: Role[]) => boolean;
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@firecms/user_management",
|
3
3
|
"type": "module",
|
4
|
-
"version": "3.0.0-canary.
|
4
|
+
"version": "3.0.0-canary.146",
|
5
5
|
"publishConfig": {
|
6
6
|
"access": "public"
|
7
7
|
},
|
@@ -29,9 +29,9 @@
|
|
29
29
|
"types": "dist/index.d.ts",
|
30
30
|
"source": "src/index.ts",
|
31
31
|
"dependencies": {
|
32
|
-
"@firecms/core": "^3.0.0-canary.
|
33
|
-
"@firecms/formex": "^3.0.0-canary.
|
34
|
-
"@firecms/ui": "^3.0.0-canary.
|
32
|
+
"@firecms/core": "^3.0.0-canary.146",
|
33
|
+
"@firecms/formex": "^3.0.0-canary.146",
|
34
|
+
"@firecms/ui": "^3.0.0-canary.146",
|
35
35
|
"date-fns": "^3.6.0"
|
36
36
|
},
|
37
37
|
"peerDependencies": {
|
@@ -55,5 +55,5 @@
|
|
55
55
|
"src",
|
56
56
|
"bin"
|
57
57
|
],
|
58
|
-
"gitHead": "
|
58
|
+
"gitHead": "601e622a79787f5b08f7ac27646d044787b972f1"
|
59
59
|
}
|
@@ -32,6 +32,7 @@ export const RoleYupSchema = Yup.object().shape({
|
|
32
32
|
|
33
33
|
function canRoleBeEdited(loggedUser: User) {
|
34
34
|
const loggedUserIsAdmin = loggedUser.roles?.map(r => r.id).includes("admin");
|
35
|
+
console.log("loggedUserIsAdmin", loggedUser);
|
35
36
|
if (!loggedUserIsAdmin) {
|
36
37
|
throw new Error("Only admins can edit roles");
|
37
38
|
}
|
@@ -54,7 +54,6 @@ export function UsersTable({ onUserClicked }: {
|
|
54
54
|
|
55
55
|
<TableHeader>
|
56
56
|
<TableCell className="truncate w-16"></TableCell>
|
57
|
-
<TableCell>ID</TableCell>
|
58
57
|
<TableCell>Email</TableCell>
|
59
58
|
<TableCell>Name</TableCell>
|
60
59
|
<TableCell>Roles</TableCell>
|
@@ -88,7 +87,6 @@ export function UsersTable({ onUserClicked }: {
|
|
88
87
|
</IconButton>
|
89
88
|
</Tooltip>
|
90
89
|
</TableCell>
|
91
|
-
<TableCell>{user.uid}</TableCell>
|
92
90
|
<TableCell>{user.email}</TableCell>
|
93
91
|
<TableCell className={"font-medium align-left"}>{user.displayName}</TableCell>
|
94
92
|
<TableCell className="align-left">
|
@@ -3,6 +3,7 @@ import equal from "react-fast-compare"
|
|
3
3
|
|
4
4
|
import { UserManagement } from "../types";
|
5
5
|
import {
|
6
|
+
AuthController,
|
6
7
|
Authenticator,
|
7
8
|
DataSourceDelegate,
|
8
9
|
Entity,
|
@@ -13,9 +14,12 @@ import {
|
|
13
14
|
} from "@firecms/core";
|
14
15
|
import { resolveUserRolePermissions } from "../utils";
|
15
16
|
|
16
|
-
type UserWithRoleIds = Omit<
|
17
|
+
type UserWithRoleIds<USER extends User = any> = Omit<USER, "roles"> & { roles: string[] };
|
17
18
|
|
18
|
-
export interface UserManagementParams
|
19
|
+
export interface UserManagementParams<CONTROLLER extends AuthController<any> = AuthController<any>,
|
20
|
+
USER extends User = CONTROLLER extends AuthController<infer U> ? U : any> {
|
21
|
+
|
22
|
+
authController: CONTROLLER;
|
19
23
|
|
20
24
|
/**
|
21
25
|
* The delegate in charge of persisting the data.
|
@@ -62,6 +66,7 @@ export interface UserManagementParams {
|
|
62
66
|
/**
|
63
67
|
* This hook is used to build a user management object that can be used to
|
64
68
|
* manage users and roles in a Firestore backend.
|
69
|
+
* @param authController
|
65
70
|
* @param dataSourceDelegate
|
66
71
|
* @param usersPath
|
67
72
|
* @param rolesPath
|
@@ -70,38 +75,49 @@ export interface UserManagementParams {
|
|
70
75
|
* @param allowDefaultRolesCreation
|
71
76
|
* @param includeCollectionConfigPermissions
|
72
77
|
*/
|
73
|
-
export function useBuildUserManagement
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
export function useBuildUserManagement<CONTROLLER extends AuthController<any> = AuthController<any>,
|
79
|
+
USER extends User = CONTROLLER extends AuthController<infer U> ? U : any>
|
80
|
+
({
|
81
|
+
authController,
|
82
|
+
dataSourceDelegate,
|
83
|
+
usersPath = "__FIRECMS/config/users",
|
84
|
+
rolesPath = "__FIRECMS/config/roles",
|
85
|
+
usersLimit,
|
86
|
+
canEditRoles = true,
|
87
|
+
allowDefaultRolesCreation,
|
88
|
+
includeCollectionConfigPermissions
|
89
|
+
}: UserManagementParams<CONTROLLER, USER>): UserManagement<USER> & CONTROLLER {
|
90
|
+
|
91
|
+
if (!authController) {
|
92
|
+
throw Error("useBuildUserManagement: You need to provide an authController since version 3.0.0-beta.11. Check https://firecms.co/docs/pro/migrating_from_v3_beta");
|
93
|
+
}
|
82
94
|
|
83
95
|
const [rolesLoading, setRolesLoading] = React.useState<boolean>(true);
|
84
96
|
const [usersLoading, setUsersLoading] = React.useState<boolean>(true);
|
85
97
|
const [roles, setRoles] = React.useState<Role[]>([]);
|
86
|
-
const [usersWithRoleIds, setUsersWithRoleIds] = React.useState<UserWithRoleIds[]>([]);
|
98
|
+
const [usersWithRoleIds, setUsersWithRoleIds] = React.useState<UserWithRoleIds<USER>[]>([]);
|
87
99
|
|
88
100
|
const users = usersWithRoleIds.map(u => ({
|
89
101
|
...u,
|
90
102
|
roles: roles.filter(r => u.roles?.includes(r.id))
|
91
|
-
}) as
|
103
|
+
}) as USER);
|
92
104
|
|
93
105
|
const [rolesError, setRolesError] = React.useState<Error | undefined>();
|
94
106
|
const [usersError, setUsersError] = React.useState<Error | undefined>();
|
95
107
|
|
96
|
-
const
|
108
|
+
const _usersLoading = usersLoading;
|
109
|
+
const _rolesLoading = rolesLoading;
|
110
|
+
|
111
|
+
const loading = _rolesLoading || _usersLoading;
|
97
112
|
|
98
113
|
useEffect(() => {
|
99
114
|
if (!dataSourceDelegate || !rolesPath) return;
|
100
115
|
if (dataSourceDelegate.initialised !== undefined && !dataSourceDelegate.initialised) return;
|
101
|
-
if (
|
102
|
-
|
103
|
-
|
104
|
-
|
116
|
+
if (authController?.initialLoading) return;
|
117
|
+
// if (authController.user === null) {
|
118
|
+
// setRolesLoading(false);
|
119
|
+
// return;
|
120
|
+
// }
|
105
121
|
|
106
122
|
setRolesLoading(true);
|
107
123
|
return dataSourceDelegate.listenCollection?.({
|
@@ -110,8 +126,9 @@ export function useBuildUserManagement({
|
|
110
126
|
setRolesError(undefined);
|
111
127
|
try {
|
112
128
|
const newRoles = entityToRoles(entities);
|
113
|
-
if (!equal(newRoles, roles))
|
129
|
+
if (!equal(newRoles, roles)) {
|
114
130
|
setRoles(newRoles);
|
131
|
+
}
|
115
132
|
} catch (e) {
|
116
133
|
setRoles([]);
|
117
134
|
console.error("Error loading roles", e);
|
@@ -127,25 +144,31 @@ export function useBuildUserManagement({
|
|
127
144
|
}
|
128
145
|
});
|
129
146
|
|
130
|
-
}, [dataSourceDelegate?.initialised,
|
147
|
+
}, [dataSourceDelegate?.initialised, authController?.initialLoading, authController?.user?.uid, rolesPath]);
|
131
148
|
|
132
149
|
useEffect(() => {
|
133
150
|
if (!dataSourceDelegate || !usersPath) return;
|
134
|
-
if (dataSourceDelegate.initialised !== undefined && !dataSourceDelegate.initialised)
|
135
|
-
if (dataSourceDelegate.authenticated !== undefined && !dataSourceDelegate.authenticated) {
|
136
|
-
setUsersLoading(false);
|
151
|
+
if (dataSourceDelegate.initialised !== undefined && !dataSourceDelegate.initialised) {
|
137
152
|
return;
|
138
153
|
}
|
154
|
+
if (authController?.initialLoading) {
|
155
|
+
return;
|
156
|
+
}
|
157
|
+
// if (authController.user === null) {
|
158
|
+
// setUsersLoading(false);
|
159
|
+
// return;
|
160
|
+
// }
|
139
161
|
|
140
162
|
setUsersLoading(true);
|
141
163
|
return dataSourceDelegate.listenCollection?.({
|
142
164
|
path: usersPath,
|
143
165
|
onUpdate(entities: Entity<any>[]): void {
|
166
|
+
console.debug("Updating users", entities);
|
144
167
|
setUsersError(undefined);
|
145
168
|
try {
|
146
169
|
const newUsers = entitiesToUsers(entities);
|
147
|
-
if (!equal(newUsers, usersWithRoleIds))
|
148
|
-
|
170
|
+
// if (!equal(newUsers, usersWithRoleIds))
|
171
|
+
setUsersWithRoleIds(newUsers);
|
149
172
|
} catch (e) {
|
150
173
|
setUsersWithRoleIds([]);
|
151
174
|
console.error("Error loading users", e);
|
@@ -154,16 +177,16 @@ export function useBuildUserManagement({
|
|
154
177
|
setUsersLoading(false);
|
155
178
|
},
|
156
179
|
onError(e: any): void {
|
157
|
-
setUsersWithRoleIds([]);
|
158
180
|
console.error("Error loading users", e);
|
181
|
+
setUsersWithRoleIds([]);
|
159
182
|
setUsersError(e);
|
160
183
|
setUsersLoading(false);
|
161
184
|
}
|
162
185
|
});
|
163
186
|
|
164
|
-
}, [dataSourceDelegate?.initialised,
|
187
|
+
}, [dataSourceDelegate?.initialised, authController?.initialLoading, authController?.user?.uid, usersPath]);
|
165
188
|
|
166
|
-
const saveUser = useCallback(async (user:
|
189
|
+
const saveUser = useCallback(async (user: USER): Promise<USER> => {
|
167
190
|
if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
|
168
191
|
if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
|
169
192
|
|
@@ -238,39 +261,55 @@ export function useBuildUserManagement({
|
|
238
261
|
const collectionPermissions: PermissionsBuilder = useCallback(({
|
239
262
|
collection,
|
240
263
|
user
|
241
|
-
}) =>
|
242
|
-
|
243
|
-
|
244
|
-
|
264
|
+
}) =>
|
265
|
+
resolveUserRolePermissions({
|
266
|
+
collection,
|
267
|
+
user
|
268
|
+
}), []);
|
245
269
|
|
246
270
|
const defineRolesFor: ((user: User) => Role[] | undefined) = useCallback((user) => {
|
247
|
-
if (!
|
271
|
+
if (!usersWithRoleIds) throw Error("Users not loaded");
|
272
|
+
const users = usersWithRoleIds.map(u => ({
|
273
|
+
...u,
|
274
|
+
roles: roles.filter(r => u.roles?.includes(r.id))
|
275
|
+
}) as User);
|
248
276
|
const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
|
249
277
|
return mgmtUser?.roles;
|
250
|
-
}, [
|
251
|
-
|
252
|
-
const authenticator: Authenticator = useCallback(({ user }) => {
|
253
|
-
console.debug("Authenticating user", user);
|
278
|
+
}, [roles, usersWithRoleIds]);
|
254
279
|
|
280
|
+
const authenticator: Authenticator<USER> = useCallback(({ user }) => {
|
255
281
|
if (loading) {
|
256
|
-
console.warn("User management is still loading");
|
257
282
|
return false;
|
258
283
|
}
|
259
284
|
|
260
|
-
// This is an example of how you can link the access system to the user management plugin
|
261
285
|
if (users.length === 0) {
|
286
|
+
console.warn("No users created yet");
|
262
287
|
return true; // If there are no users created yet, we allow access to every user
|
263
288
|
}
|
264
289
|
|
265
290
|
const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
|
266
291
|
if (mgmtUser) {
|
292
|
+
console.debug("User found in user management system", mgmtUser);
|
267
293
|
return true;
|
268
294
|
}
|
269
295
|
|
270
296
|
throw Error("Could not find a user with the provided email in the user management system.");
|
271
|
-
}, [loading, users
|
272
|
-
|
273
|
-
const
|
297
|
+
}, [loading, users]);
|
298
|
+
|
299
|
+
const userRoles = authController.user ? defineRolesFor(authController.user) : undefined;
|
300
|
+
const isAdmin = (userRoles ?? []).some(r => r.id === "admin");
|
301
|
+
|
302
|
+
// console.log("Setting roles", {
|
303
|
+
// user: authController.user,
|
304
|
+
// userRoles
|
305
|
+
// });
|
306
|
+
// useEffect(() => {
|
307
|
+
// console.debug("Setting roles", {
|
308
|
+
// authController,
|
309
|
+
// userRoles
|
310
|
+
// });
|
311
|
+
// authController.setUserRoles?.(userRoles ?? []);
|
312
|
+
// }, [userRoles?.map(r => r.id)]);
|
274
313
|
|
275
314
|
return {
|
276
315
|
loading,
|
@@ -289,7 +328,14 @@ export function useBuildUserManagement({
|
|
289
328
|
includeCollectionConfigPermissions: Boolean(includeCollectionConfigPermissions),
|
290
329
|
collectionPermissions,
|
291
330
|
defineRolesFor,
|
292
|
-
authenticator
|
331
|
+
authenticator,
|
332
|
+
...authController,
|
333
|
+
initialLoading: authController.initialLoading || loading,
|
334
|
+
userRoles: userRoles,
|
335
|
+
user: authController.user ? {
|
336
|
+
...authController.user,
|
337
|
+
roles: userRoles
|
338
|
+
} : null
|
293
339
|
}
|
294
340
|
}
|
295
341
|
|
@@ -2,6 +2,8 @@ import { Authenticator, PermissionsBuilder, Role, User } from "@firecms/core";
|
|
2
2
|
|
3
3
|
export type UserManagement<USER extends User = User> = {
|
4
4
|
|
5
|
+
authenticator?: Authenticator<USER>;
|
6
|
+
|
5
7
|
loading: boolean;
|
6
8
|
|
7
9
|
users: USER[];
|
@@ -49,12 +51,6 @@ export type UserManagement<USER extends User = User> = {
|
|
49
51
|
*/
|
50
52
|
defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
|
51
53
|
|
52
|
-
/**
|
53
|
-
* You can build an authenticator callback from the current configuration of the user management.
|
54
|
-
* It will only allow access to users with the required roles.
|
55
|
-
*/
|
56
|
-
authenticator?: Authenticator;
|
57
|
-
|
58
54
|
rolesError?: Error;
|
59
55
|
usersError?: Error;
|
60
56
|
|
@@ -1,11 +1,11 @@
|
|
1
|
-
import { FireCMSPlugin, useAuthController, useSnackbarController } from "@firecms/core";
|
1
|
+
import { FireCMSPlugin, useAuthController, User, useSnackbarController } from "@firecms/core";
|
2
2
|
import { UserManagementProvider } from "./UserManagementProvider";
|
3
|
-
import {
|
3
|
+
import { UserManagement } from "./types";
|
4
4
|
import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
|
5
5
|
import { DEFAULT_ROLES } from "./components/roles/default_roles";
|
6
6
|
|
7
|
-
export function useUserManagementPlugin({ userManagement }: {
|
8
|
-
userManagement: UserManagement
|
7
|
+
export function useUserManagementPlugin<USER extends User = any>({ userManagement }: {
|
8
|
+
userManagement: UserManagement<USER>,
|
9
9
|
}): FireCMSPlugin {
|
10
10
|
|
11
11
|
const noUsers = userManagement.users.length === 0;
|
@@ -39,7 +39,7 @@ export function IntroWidget({
|
|
39
39
|
}: {
|
40
40
|
noUsers: boolean;
|
41
41
|
noRoles: boolean;
|
42
|
-
userManagement: UserManagement<
|
42
|
+
userManagement: UserManagement<any>;
|
43
43
|
}) {
|
44
44
|
|
45
45
|
const authController = useAuthController();
|
@@ -53,7 +53,7 @@ export function IntroWidget({
|
|
53
53
|
|
54
54
|
return (
|
55
55
|
<Paper
|
56
|
-
className={"my-4 flex flex-col px-4 py-6 bg-white dark:bg-
|
56
|
+
className={"my-4 flex flex-col px-4 py-6 bg-white dark:bg-surface-accent-800 gap-2"}>
|
57
57
|
<Typography variant={"subtitle2"} className={"uppercase"}>Create your users and roles</Typography>
|
58
58
|
<Typography>
|
59
59
|
You have no users or roles defined. You can create default roles and add the current user as admin.
|
package/src/utils/permissions.ts
CHANGED
@@ -9,13 +9,13 @@ const DEFAULT_PERMISSIONS = {
|
|
9
9
|
delete: false
|
10
10
|
};
|
11
11
|
|
12
|
-
export function resolveUserRolePermissions<
|
12
|
+
export function resolveUserRolePermissions<USER extends User>
|
13
13
|
({
|
14
14
|
collection,
|
15
15
|
user
|
16
16
|
}: {
|
17
17
|
collection: EntityCollection<any>,
|
18
|
-
user:
|
18
|
+
user: USER | null
|
19
19
|
}): Permissions {
|
20
20
|
|
21
21
|
const roles = user?.roles;
|