@firecms/user_management 3.0.0-canary.40 → 3.0.0-canary.42

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.
@@ -11,7 +11,7 @@ import {
11
11
  } from "firebase/firestore";
12
12
  import { FirebaseApp } from "firebase/app";
13
13
  import { UserManagement } from "../types";
14
- import { Authenticator, PermissionsBuilder, Role, User, useTraceUpdate } from "@firecms/core";
14
+ import { Authenticator, PermissionsBuilder, Role, User } from "@firecms/core";
15
15
  import { resolveUserRolePermissions } from "../utils";
16
16
 
17
17
  type UserWithRoleIds = User & { roles: string[] };
@@ -37,8 +37,14 @@ export interface UserManagementParams {
37
37
  */
38
38
  rolesPath?: string;
39
39
 
40
+ /**
41
+ * Maximum number of users that can be created.
42
+ */
40
43
  usersLimit?: number;
41
44
 
45
+ /**
46
+ * Can the logged user edit roles
47
+ */
42
48
  canEditRoles?: boolean;
43
49
 
44
50
  /**
@@ -62,15 +68,15 @@ export interface UserManagementParams {
62
68
  * @param usersLimit
63
69
  * @param canEditRoles
64
70
  */
65
- export function useBuildFirestoreUserManagement({
66
- firebaseApp,
67
- usersPath = "__FIRECMS/config/users",
68
- rolesPath = "__FIRECMS/config/roles",
69
- usersLimit,
70
- canEditRoles = true,
71
- allowDefaultRolesCreation,
72
- includeCollectionConfigPermissions
73
- }: UserManagementParams): UserManagement {
71
+ export function useFirestoreUserManagement({
72
+ firebaseApp,
73
+ usersPath = "__FIRECMS/config/users",
74
+ rolesPath = "__FIRECMS/config/roles",
75
+ usersLimit,
76
+ canEditRoles = true,
77
+ allowDefaultRolesCreation,
78
+ includeCollectionConfigPermissions
79
+ }: UserManagementParams): UserManagement {
74
80
 
75
81
  const [rolesLoading, setRolesLoading] = React.useState<boolean>(true);
76
82
  const [usersLoading, setUsersLoading] = React.useState<boolean>(true);
@@ -99,12 +105,13 @@ export function useBuildFirestoreUserManagement({
99
105
  const newRoles = docsToRoles(snapshot.docs);
100
106
  setRoles(newRoles);
101
107
  } catch (e) {
102
- // console.error(e);
108
+ console.error("Error loading roles", e);
103
109
  setRolesError(e as Error);
104
110
  }
105
111
  setRolesLoading(false);
106
112
  },
107
113
  error: (e) => {
114
+ console.error("Error loading roles", e);
108
115
  setRolesError(e);
109
116
  setRolesLoading(false);
110
117
  }
@@ -124,11 +131,13 @@ export function useBuildFirestoreUserManagement({
124
131
  const newUsers = docsToUsers(snapshot.docs);
125
132
  setUsersWithRoleIds(newUsers);
126
133
  } catch (e) {
134
+ console.error("Error loading users", e);
127
135
  setUsersError(e as Error);
128
136
  }
129
137
  setUsersLoading(false);
130
138
  },
131
139
  error: (e) => {
140
+ console.error("Error loading users", e);
132
141
  setUsersError(e);
133
142
  setUsersLoading(false);
134
143
  }
@@ -137,9 +146,9 @@ export function useBuildFirestoreUserManagement({
137
146
  }, [firebaseApp, usersPath]);
138
147
 
139
148
  const saveUser = useCallback(async (user: User): Promise<User> => {
140
- if (!firebaseApp) throw Error("useFirestoreConfigurationPersistence Firebase not initialised");
149
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
141
150
  const firestore = getFirestore(firebaseApp);
142
- if (!firestore || !usersPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
151
+ if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
143
152
  console.debug("Persisting user", user);
144
153
  const roleIds = user.roles?.map(r => r.id);
145
154
  const {
@@ -155,12 +164,12 @@ export function useBuildFirestoreUserManagement({
155
164
  } else {
156
165
  return addDoc(collection(firestore, usersPath), data).then(() => user);
157
166
  }
158
- }, [usersPath]);
167
+ }, [usersPath, firebaseApp]);
159
168
 
160
169
  const saveRole = useCallback((role: Role): Promise<void> => {
161
- if (!firebaseApp) throw Error("useFirestoreConfigurationPersistence Firebase not initialised");
170
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
162
171
  const firestore = getFirestore(firebaseApp);
163
- if (!firestore || !rolesPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
172
+ if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
164
173
  console.debug("Persisting role", role);
165
174
  const {
166
175
  id,
@@ -168,26 +177,26 @@ export function useBuildFirestoreUserManagement({
168
177
  } = role;
169
178
  const ref = doc(firestore, rolesPath, id);
170
179
  return setDoc(ref, roleData, { merge: true });
171
- }, [rolesPath]);
180
+ }, [rolesPath, firebaseApp]);
172
181
 
173
182
  const deleteUser = useCallback(async (user: User): Promise<void> => {
174
- if (!firebaseApp) throw Error("useFirestoreConfigurationPersistence Firebase not initialised");
183
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
175
184
  const firestore = getFirestore(firebaseApp);
176
- if (!firestore || !usersPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
185
+ if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
177
186
  console.debug("Deleting", user);
178
187
  const { uid } = user;
179
188
  return deleteDoc(doc(firestore, usersPath, uid));
180
- }, [usersPath]);
189
+ }, [usersPath, firebaseApp]);
181
190
 
182
191
  const deleteRole = useCallback((role: Role): Promise<void> => {
183
- if (!firebaseApp) throw Error("useFirestoreConfigurationPersistence Firebase not initialised");
192
+ if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
184
193
  const firestore = getFirestore(firebaseApp);
185
- if (!firestore || !rolesPath) throw Error("useFirestoreConfigurationPersistence Firestore not initialised");
194
+ if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
186
195
  console.debug("Deleting", role);
187
196
  const { id } = role;
188
197
  const ref = doc(firestore, rolesPath, id);
189
198
  return deleteDoc(ref);
190
- }, [rolesPath]);
199
+ }, [rolesPath, firebaseApp]);
191
200
 
192
201
  const collectionPermissions: PermissionsBuilder = useCallback(({
193
202
  collection,
@@ -197,20 +206,17 @@ export function useBuildFirestoreUserManagement({
197
206
  user
198
207
  }), []);
199
208
 
200
- const userIds = users.map(u => u.uid);
201
209
  const defineRolesFor: ((user: User) => Role[] | undefined) = useCallback((user) => {
202
210
  if (!users) throw Error("Users not loaded");
203
211
  const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
204
212
  return mgmtUser?.roles;
205
- }, [userIds]);
213
+ }, [users]);
206
214
 
207
215
  const authenticator: Authenticator = useCallback(({ user }) => {
208
- console.log("Authenticating user", user);
209
- // return true;"
216
+ console.debug("Authenticating user", user);
210
217
 
211
- // console.log("authentication", user, userManagement);
212
218
  if (loading) {
213
- console.log("User management is still loading");
219
+ console.warn("User management is still loading");
214
220
  return false;
215
221
  }
216
222
 
@@ -221,12 +227,11 @@ export function useBuildFirestoreUserManagement({
221
227
 
222
228
  const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
223
229
  if (mgmtUser) {
224
- // authController.setRoles(mgmtUser.roles ?? [])
225
230
  return true;
226
231
  }
227
232
 
228
- throw Error("Could not find a user with the provided email");
229
- }, [loading, userIds])
233
+ throw Error("Could not find a user with the provided email in the user management system.");
234
+ }, [loading, users])
230
235
 
231
236
  return {
232
237
  loading,
@@ -234,9 +239,11 @@ export function useBuildFirestoreUserManagement({
234
239
  users,
235
240
  saveUser,
236
241
  saveRole,
242
+ rolesError,
237
243
  deleteUser,
238
244
  deleteRole,
239
245
  usersLimit,
246
+ usersError,
240
247
  canEditRoles: canEditRoles === undefined ? true : canEditRoles,
241
248
  allowDefaultRolesCreation: allowDefaultRolesCreation === undefined ? true : allowDefaultRolesCreation,
242
249
  includeCollectionConfigPermissions: Boolean(includeCollectionConfigPermissions),
@@ -42,7 +42,7 @@ export type UserManagement<USER extends User = User> = {
42
42
  * Define the roles for a given user. You will typically want to plug this into your auth controller.
43
43
  * @param user
44
44
  */
45
- defineRolesFor: (user: User) => Promise<Role[]> | Role[] | undefined;
45
+ defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
46
46
 
47
47
  /**
48
48
  * You can build an authenticator callback from the current configuration of the user management.
@@ -50,4 +50,7 @@ export type UserManagement<USER extends User = User> = {
50
50
  */
51
51
  authenticator?: Authenticator;
52
52
 
53
+ rolesError?: Error;
54
+ usersError?: Error;
55
+
53
56
  };
@@ -1,13 +1,28 @@
1
- import { FireCMSPlugin } from "@firecms/core";
1
+ import { FireCMSPlugin, useAuthController, useSnackbarController } from "@firecms/core";
2
2
  import { UserManagementProvider } from "./UserManagementProvider";
3
- import { UserManagement } from "./types";
3
+ import { PersistedUser, UserManagement } from "./types";
4
+ import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
5
+ import { DEFAULT_ROLES } from "./components/roles/default_roles";
4
6
 
5
7
  export function useUserManagementPlugin({ userManagement }: {
6
8
  userManagement: UserManagement,
7
9
  }): FireCMSPlugin {
10
+
11
+ const noUsers = userManagement.users.length === 0;
12
+ const noRoles = userManagement.roles.length === 0;
13
+
8
14
  return {
9
15
  key: "user_management",
10
16
  loading: userManagement.loading,
17
+
18
+ homePage: {
19
+ additionalChildrenStart: noUsers || noRoles
20
+ ? <IntroWidget
21
+ noUsers={noUsers}
22
+ noRoles={noRoles}
23
+ userManagement={userManagement}/>
24
+ : undefined
25
+ },
11
26
  provider: {
12
27
  Component: UserManagementProvider,
13
28
  props: {
@@ -16,3 +31,74 @@ export function useUserManagementPlugin({ userManagement }: {
16
31
  }
17
32
  }
18
33
  }
34
+
35
+ export function IntroWidget({
36
+ noUsers,
37
+ noRoles,
38
+ userManagement
39
+ }: {
40
+ noUsers: boolean;
41
+ noRoles: boolean;
42
+ userManagement: UserManagement<PersistedUser>;
43
+ }) {
44
+
45
+ const authController = useAuthController();
46
+ const snackbarController = useSnackbarController();
47
+
48
+ const buttonLabel = noUsers && noRoles
49
+ ? "Create default roles and add current user as admin"
50
+ : noUsers
51
+ ? "Add current user as admin"
52
+ : noRoles ? "Create default roles" : undefined;
53
+
54
+ return (
55
+ <Paper
56
+ className={"my-4 flex flex-col px-4 py-6 bg-white dark:bg-slate-800 gap-2"}>
57
+ <Typography variant={"subtitle2"} className={"uppercase"}>Create your users and roles</Typography>
58
+ <Typography>
59
+ You have no users or roles defined. You can create default roles and add the current user as admin.
60
+ </Typography>
61
+ <Button onClick={() => {
62
+ if (!authController.user?.uid) {
63
+ throw Error("UsersTable, authController misconfiguration");
64
+ }
65
+ if (noUsers) {
66
+ userManagement.saveUser({
67
+ uid: authController.user?.uid,
68
+ email: authController.user?.email,
69
+ displayName: authController.user?.displayName,
70
+ photoURL: authController.user?.photoURL,
71
+ providerId: authController.user?.providerId,
72
+ isAnonymous: authController.user?.isAnonymous,
73
+ roles: [{
74
+ id: "admin",
75
+ name: "Admin"
76
+ }],
77
+ created_on: new Date()
78
+ })
79
+ .then(() => {
80
+ snackbarController.open({
81
+ type: "success",
82
+ message: "User added successfully"
83
+ })
84
+ })
85
+ .catch((error) => {
86
+ snackbarController.open({
87
+ type: "error",
88
+ message: "Error adding user: " + error.message
89
+ })
90
+ });
91
+ }
92
+ if (noRoles) {
93
+ DEFAULT_ROLES.forEach((role) => {
94
+ userManagement.saveRole(role);
95
+ });
96
+ }
97
+ }}>
98
+ <AddIcon/>
99
+ {buttonLabel}
100
+ </Button>
101
+ </Paper>
102
+ );
103
+
104
+ }