@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.
- package/LICENSE +113 -21
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/{useBuildFirestoreUserManagement.d.ts → useFirestoreUserManagement.d.ts} +7 -1
- package/dist/index.es.js +555 -492
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/user_management.d.ts +3 -1
- package/dist/useUserManagementPlugin.d.ts +6 -1
- package/package.json +5 -6
- package/src/hooks/index.ts +1 -1
- package/src/hooks/{useBuildFirestoreUserManagement.tsx → useFirestoreUserManagement.tsx} +39 -32
- package/src/types/user_management.tsx +4 -1
- package/src/useUserManagementPlugin.tsx +88 -2
@@ -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
|
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
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
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("
|
149
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
141
150
|
const firestore = getFirestore(firebaseApp);
|
142
|
-
if (!firestore || !usersPath) throw Error("
|
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("
|
170
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
162
171
|
const firestore = getFirestore(firebaseApp);
|
163
|
-
if (!firestore || !rolesPath) throw Error("
|
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("
|
183
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
175
184
|
const firestore = getFirestore(firebaseApp);
|
176
|
-
if (!firestore || !usersPath) throw Error("
|
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("
|
192
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
184
193
|
const firestore = getFirestore(firebaseApp);
|
185
|
-
if (!firestore || !rolesPath) throw Error("
|
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
|
-
}, [
|
213
|
+
}, [users]);
|
206
214
|
|
207
215
|
const authenticator: Authenticator = useCallback(({ user }) => {
|
208
|
-
console.
|
209
|
-
// return true;"
|
216
|
+
console.debug("Authenticating user", user);
|
210
217
|
|
211
|
-
// console.log("authentication", user, userManagement);
|
212
218
|
if (loading) {
|
213
|
-
console.
|
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,
|
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
|
+
}
|