@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.
@@ -1,10 +1,10 @@
1
- import { FireCMSPlugin } from "@firecms/core";
2
- import { PersistedUser, UserManagement } from "./types";
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<PersistedUser>;
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<UserType extends User>({ collection, user }: {
3
+ export declare function resolveUserRolePermissions<USER extends User>({ collection, user }: {
4
4
  collection: EntityCollection<any>;
5
- user: UserType | null;
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.144",
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.144",
33
- "@firecms/formex": "^3.0.0-canary.144",
34
- "@firecms/ui": "^3.0.0-canary.144",
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": "8f862289db623ffd350e61e4ab4a06957ed97bdf"
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<User, "roles"> & { roles: string[] };
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
- dataSourceDelegate,
75
- usersPath = "__FIRECMS/config/users",
76
- rolesPath = "__FIRECMS/config/roles",
77
- usersLimit,
78
- canEditRoles = true,
79
- allowDefaultRolesCreation,
80
- includeCollectionConfigPermissions
81
- }: UserManagementParams): UserManagement {
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 User);
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 loading = rolesLoading || usersLoading;
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 (dataSourceDelegate.authenticated !== undefined && !dataSourceDelegate.authenticated) {
102
- setRolesLoading(false);
103
- return;
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, dataSourceDelegate?.authenticated, rolesPath]);
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) return;
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
- setUsersWithRoleIds(newUsers);
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, dataSourceDelegate?.authenticated, usersPath]);
187
+ }, [dataSourceDelegate?.initialised, authController?.initialLoading, authController?.user?.uid, usersPath]);
165
188
 
166
- const saveUser = useCallback(async (user: User): Promise<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
- }) => resolveUserRolePermissions({
242
- collection,
243
- user
244
- }), []);
264
+ }) =>
265
+ resolveUserRolePermissions({
266
+ collection,
267
+ user
268
+ }), []);
245
269
 
246
270
  const defineRolesFor: ((user: User) => Role[] | undefined) = useCallback((user) => {
247
- if (!users) throw Error("Users not loaded");
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
- }, [users]);
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, usersError, rolesError]);
272
-
273
- const isAdmin = roles.some(r => r.id === "admin");
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 { PersistedUser, UserManagement } from "./types";
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<PersistedUser>;
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-slate-800 gap-2"}>
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.
@@ -9,13 +9,13 @@ const DEFAULT_PERMISSIONS = {
9
9
  delete: false
10
10
  };
11
11
 
12
- export function resolveUserRolePermissions<UserType extends User>
12
+ export function resolveUserRolePermissions<USER extends User>
13
13
  ({
14
14
  collection,
15
15
  user
16
16
  }: {
17
17
  collection: EntityCollection<any>,
18
- user: UserType | null
18
+ user: USER | null
19
19
  }): Permissions {
20
20
 
21
21
  const roles = user?.roles;