@firecms/user_management 3.0.0-3.0.0-canary.27.0 → 3.0.0-3.0.0-canary.44.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/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 +626 -557
- 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 +11 -12
- package/src/hooks/index.ts +1 -1
- package/src/hooks/{useBuildFirestoreUserManagement.tsx → useFirestoreUserManagement.tsx} +42 -39
- package/src/types/user_management.tsx +4 -1
- package/src/useUserManagementPlugin.tsx +88 -2
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-3.0.0-canary.
|
4
|
+
"version": "3.0.0-3.0.0-canary.44.0",
|
5
5
|
"publishConfig": {
|
6
6
|
"access": "public"
|
7
7
|
},
|
@@ -24,15 +24,14 @@
|
|
24
24
|
},
|
25
25
|
"./package.json": "./package.json"
|
26
26
|
},
|
27
|
-
"packageManager": "yarn@4.1.0",
|
28
27
|
"main": "./dist/index.umd.js",
|
29
28
|
"module": "./dist/index.es.js",
|
30
29
|
"types": "dist/index.d.ts",
|
31
30
|
"source": "src/index.ts",
|
32
31
|
"dependencies": {
|
33
|
-
"@firecms/core": "^3.0.0-3.0.0-canary.
|
34
|
-
"@firecms/formex": "^3.0.0-3.0.0-canary.
|
35
|
-
"@firecms/ui": "^3.0.0-3.0.0-canary.
|
32
|
+
"@firecms/core": "^3.0.0-3.0.0-canary.44.0",
|
33
|
+
"@firecms/formex": "^3.0.0-3.0.0-canary.44.0",
|
34
|
+
"@firecms/ui": "^3.0.0-3.0.0-canary.44.0",
|
36
35
|
"date-fns": "^3.6.0"
|
37
36
|
},
|
38
37
|
"peerDependencies": {
|
@@ -41,10 +40,10 @@
|
|
41
40
|
"react-dom": "^18.2.0"
|
42
41
|
},
|
43
42
|
"devDependencies": {
|
44
|
-
"@types/node": "^20.
|
45
|
-
"@types/react": "^18.2.
|
46
|
-
"@types/react-dom": "^18.2.
|
47
|
-
"@typescript-eslint/parser": "^7.
|
43
|
+
"@types/node": "^20.12.7",
|
44
|
+
"@types/react": "^18.2.79",
|
45
|
+
"@types/react-dom": "^18.2.25",
|
46
|
+
"@typescript-eslint/parser": "^7.7.0",
|
48
47
|
"eslint": "^8.57.0",
|
49
48
|
"eslint-config-standard": "^17.1.0",
|
50
49
|
"eslint-plugin-import": "^2.29.1",
|
@@ -52,8 +51,8 @@
|
|
52
51
|
"eslint-plugin-promise": "^6.1.1",
|
53
52
|
"eslint-plugin-react": "^7.34.1",
|
54
53
|
"eslint-plugin-react-hooks": "^4.6.0",
|
55
|
-
"typescript": "^5.4.
|
56
|
-
"vite": "^5.2.
|
54
|
+
"typescript": "^5.4.5",
|
55
|
+
"vite": "^5.2.9"
|
57
56
|
},
|
58
57
|
"scripts": {
|
59
58
|
"dev": "vite",
|
@@ -71,5 +70,5 @@
|
|
71
70
|
"react-app/jest"
|
72
71
|
]
|
73
72
|
},
|
74
|
-
"gitHead": "
|
73
|
+
"gitHead": "ed16b40251cd10faff8af7fe7b6817aa5deee02b"
|
75
74
|
}
|
package/src/hooks/index.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export * from "./
|
1
|
+
export * from "./useFirestoreUserManagement";
|
2
2
|
export * from "./useUserManagement";
|
@@ -1,11 +1,10 @@
|
|
1
|
-
import React, { useCallback, useEffect
|
1
|
+
import React, { useCallback, useEffect } from "react";
|
2
2
|
import {
|
3
3
|
addDoc,
|
4
4
|
collection,
|
5
5
|
deleteDoc,
|
6
6
|
doc,
|
7
7
|
DocumentSnapshot,
|
8
|
-
Firestore,
|
9
8
|
getFirestore,
|
10
9
|
onSnapshot,
|
11
10
|
setDoc
|
@@ -38,8 +37,14 @@ export interface UserManagementParams {
|
|
38
37
|
*/
|
39
38
|
rolesPath?: string;
|
40
39
|
|
40
|
+
/**
|
41
|
+
* Maximum number of users that can be created.
|
42
|
+
*/
|
41
43
|
usersLimit?: number;
|
42
44
|
|
45
|
+
/**
|
46
|
+
* Can the logged user edit roles
|
47
|
+
*/
|
43
48
|
canEditRoles?: boolean;
|
44
49
|
|
45
50
|
/**
|
@@ -63,17 +68,15 @@ export interface UserManagementParams {
|
|
63
68
|
* @param usersLimit
|
64
69
|
* @param canEditRoles
|
65
70
|
*/
|
66
|
-
export function
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
const firestoreRef = useRef<Firestore>();
|
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 {
|
77
80
|
|
78
81
|
const [rolesLoading, setRolesLoading] = React.useState<boolean>(true);
|
79
82
|
const [usersLoading, setUsersLoading] = React.useState<boolean>(true);
|
@@ -90,11 +93,6 @@ export function useBuildFirestoreUserManagement({
|
|
90
93
|
|
91
94
|
const loading = rolesLoading || usersLoading;
|
92
95
|
|
93
|
-
useEffect(() => {
|
94
|
-
if (!firebaseApp) return;
|
95
|
-
firestoreRef.current = getFirestore(firebaseApp);
|
96
|
-
}, [firebaseApp]);
|
97
|
-
|
98
96
|
useEffect(() => {
|
99
97
|
if (!firebaseApp || !rolesPath) return;
|
100
98
|
const firestore = getFirestore(firebaseApp);
|
@@ -107,12 +105,13 @@ export function useBuildFirestoreUserManagement({
|
|
107
105
|
const newRoles = docsToRoles(snapshot.docs);
|
108
106
|
setRoles(newRoles);
|
109
107
|
} catch (e) {
|
110
|
-
|
108
|
+
console.error("Error loading roles", e);
|
111
109
|
setRolesError(e as Error);
|
112
110
|
}
|
113
111
|
setRolesLoading(false);
|
114
112
|
},
|
115
113
|
error: (e) => {
|
114
|
+
console.error("Error loading roles", e);
|
116
115
|
setRolesError(e);
|
117
116
|
setRolesLoading(false);
|
118
117
|
}
|
@@ -132,11 +131,13 @@ export function useBuildFirestoreUserManagement({
|
|
132
131
|
const newUsers = docsToUsers(snapshot.docs);
|
133
132
|
setUsersWithRoleIds(newUsers);
|
134
133
|
} catch (e) {
|
134
|
+
console.error("Error loading users", e);
|
135
135
|
setUsersError(e as Error);
|
136
136
|
}
|
137
137
|
setUsersLoading(false);
|
138
138
|
},
|
139
139
|
error: (e) => {
|
140
|
+
console.error("Error loading users", e);
|
140
141
|
setUsersError(e);
|
141
142
|
setUsersLoading(false);
|
142
143
|
}
|
@@ -145,8 +146,9 @@ export function useBuildFirestoreUserManagement({
|
|
145
146
|
}, [firebaseApp, usersPath]);
|
146
147
|
|
147
148
|
const saveUser = useCallback(async (user: User): Promise<User> => {
|
148
|
-
|
149
|
-
|
149
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
150
|
+
const firestore = getFirestore(firebaseApp);
|
151
|
+
if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
|
150
152
|
console.debug("Persisting user", user);
|
151
153
|
const roleIds = user.roles?.map(r => r.id);
|
152
154
|
const {
|
@@ -162,11 +164,12 @@ export function useBuildFirestoreUserManagement({
|
|
162
164
|
} else {
|
163
165
|
return addDoc(collection(firestore, usersPath), data).then(() => user);
|
164
166
|
}
|
165
|
-
}, [usersPath]);
|
167
|
+
}, [usersPath, firebaseApp]);
|
166
168
|
|
167
169
|
const saveRole = useCallback((role: Role): Promise<void> => {
|
168
|
-
|
169
|
-
|
170
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
171
|
+
const firestore = getFirestore(firebaseApp);
|
172
|
+
if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
|
170
173
|
console.debug("Persisting role", role);
|
171
174
|
const {
|
172
175
|
id,
|
@@ -174,24 +177,26 @@ export function useBuildFirestoreUserManagement({
|
|
174
177
|
} = role;
|
175
178
|
const ref = doc(firestore, rolesPath, id);
|
176
179
|
return setDoc(ref, roleData, { merge: true });
|
177
|
-
}, [rolesPath]);
|
180
|
+
}, [rolesPath, firebaseApp]);
|
178
181
|
|
179
182
|
const deleteUser = useCallback(async (user: User): Promise<void> => {
|
180
|
-
|
181
|
-
|
183
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
184
|
+
const firestore = getFirestore(firebaseApp);
|
185
|
+
if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
|
182
186
|
console.debug("Deleting", user);
|
183
187
|
const { uid } = user;
|
184
188
|
return deleteDoc(doc(firestore, usersPath, uid));
|
185
|
-
}, [usersPath]);
|
189
|
+
}, [usersPath, firebaseApp]);
|
186
190
|
|
187
191
|
const deleteRole = useCallback((role: Role): Promise<void> => {
|
188
|
-
|
189
|
-
|
192
|
+
if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
|
193
|
+
const firestore = getFirestore(firebaseApp);
|
194
|
+
if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
|
190
195
|
console.debug("Deleting", role);
|
191
196
|
const { id } = role;
|
192
197
|
const ref = doc(firestore, rolesPath, id);
|
193
198
|
return deleteDoc(ref);
|
194
|
-
}, [rolesPath]);
|
199
|
+
}, [rolesPath, firebaseApp]);
|
195
200
|
|
196
201
|
const collectionPermissions: PermissionsBuilder = useCallback(({
|
197
202
|
collection,
|
@@ -201,20 +206,17 @@ export function useBuildFirestoreUserManagement({
|
|
201
206
|
user
|
202
207
|
}), []);
|
203
208
|
|
204
|
-
const userIds = users.map(u => u.uid);
|
205
209
|
const defineRolesFor: ((user: User) => Role[] | undefined) = useCallback((user) => {
|
206
210
|
if (!users) throw Error("Users not loaded");
|
207
211
|
const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
|
208
212
|
return mgmtUser?.roles;
|
209
|
-
}, [
|
213
|
+
}, [users]);
|
210
214
|
|
211
215
|
const authenticator: Authenticator = useCallback(({ user }) => {
|
212
|
-
console.
|
213
|
-
// return true;"
|
216
|
+
console.debug("Authenticating user", user);
|
214
217
|
|
215
|
-
// console.log("authentication", user, userManagement);
|
216
218
|
if (loading) {
|
217
|
-
console.
|
219
|
+
console.warn("User management is still loading");
|
218
220
|
return false;
|
219
221
|
}
|
220
222
|
|
@@ -225,11 +227,10 @@ export function useBuildFirestoreUserManagement({
|
|
225
227
|
|
226
228
|
const mgmtUser = users.find(u => u.email?.toLowerCase() === user?.email?.toLowerCase());
|
227
229
|
if (mgmtUser) {
|
228
|
-
// authController.setRoles(mgmtUser.roles ?? [])
|
229
230
|
return true;
|
230
231
|
}
|
231
232
|
|
232
|
-
throw Error("Could not find a user with the provided email");
|
233
|
+
throw Error("Could not find a user with the provided email in the user management system.");
|
233
234
|
}, [loading, users])
|
234
235
|
|
235
236
|
return {
|
@@ -238,9 +239,11 @@ export function useBuildFirestoreUserManagement({
|
|
238
239
|
users,
|
239
240
|
saveUser,
|
240
241
|
saveRole,
|
242
|
+
rolesError,
|
241
243
|
deleteUser,
|
242
244
|
deleteRole,
|
243
245
|
usersLimit,
|
246
|
+
usersError,
|
244
247
|
canEditRoles: canEditRoles === undefined ? true : canEditRoles,
|
245
248
|
allowDefaultRolesCreation: allowDefaultRolesCreation === undefined ? true : allowDefaultRolesCreation,
|
246
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
|
+
}
|