@nymphjs/tilmeld 1.0.0-beta.60 → 1.0.0-beta.62
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/CHANGELOG.md +14 -0
- package/dist/Group.d.ts +13 -4
- package/dist/Group.js +82 -33
- package/dist/Group.js.map +1 -1
- package/dist/Group.test.js +74 -0
- package/dist/Group.test.js.map +1 -1
- package/dist/User.d.ts +13 -4
- package/dist/User.js +174 -120
- package/dist/User.js.map +1 -1
- package/dist/User.test.js +60 -0
- package/dist/User.test.js.map +1 -1
- package/package.json +8 -8
- package/src/Group.test.ts +99 -1
- package/src/Group.ts +86 -21
- package/src/User.test.ts +77 -1
- package/src/User.ts +201 -132
package/src/Group.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
Nymph,
|
|
2
3
|
EntityData,
|
|
3
4
|
EntityJson,
|
|
4
5
|
EntityPatch,
|
|
@@ -17,7 +18,8 @@ import {
|
|
|
17
18
|
BadUsernameError,
|
|
18
19
|
CouldNotChangeDefaultPrimaryGroupError,
|
|
19
20
|
} from './errors';
|
|
20
|
-
import User
|
|
21
|
+
import type User from './User';
|
|
22
|
+
import type { UserData } from './User';
|
|
21
23
|
|
|
22
24
|
export type GroupData = {
|
|
23
25
|
/**
|
|
@@ -110,9 +112,13 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
110
112
|
protected $allowlistTags?: string[] = [];
|
|
111
113
|
|
|
112
114
|
/**
|
|
113
|
-
* This
|
|
115
|
+
* This should only be used by the backend.
|
|
114
116
|
*/
|
|
115
117
|
private $skipAcWhenSaving = false;
|
|
118
|
+
/**
|
|
119
|
+
* This should only be used by the backend.
|
|
120
|
+
*/
|
|
121
|
+
private $skipAcWhenDeleting = false;
|
|
116
122
|
|
|
117
123
|
static async factoryGroupname(
|
|
118
124
|
groupname?: string,
|
|
@@ -135,15 +141,6 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
135
141
|
return entity;
|
|
136
142
|
}
|
|
137
143
|
|
|
138
|
-
constructor() {
|
|
139
|
-
super();
|
|
140
|
-
|
|
141
|
-
// Defaults.
|
|
142
|
-
this.$data.enabled = true;
|
|
143
|
-
this.$data.abilities = [];
|
|
144
|
-
this.$updateDataProtection();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
144
|
/**
|
|
148
145
|
* Get all the groups that can be assigned as primary groups.
|
|
149
146
|
*
|
|
@@ -229,6 +226,42 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
229
226
|
return assignableGroups;
|
|
230
227
|
}
|
|
231
228
|
|
|
229
|
+
constructor() {
|
|
230
|
+
super();
|
|
231
|
+
|
|
232
|
+
// Defaults.
|
|
233
|
+
this.$data.enabled = true;
|
|
234
|
+
this.$data.abilities = [];
|
|
235
|
+
this.$updateDataProtection();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
$setNymph(nymph: Nymph) {
|
|
239
|
+
this.$nymph = nymph;
|
|
240
|
+
if (!this.$asleep()) {
|
|
241
|
+
if (this.$data.user && this.$data.user.$nymph !== nymph) {
|
|
242
|
+
this.$data.user.$setNymph(nymph);
|
|
243
|
+
}
|
|
244
|
+
if (this.$data.parent && this.$data.parent.$nymph !== nymph) {
|
|
245
|
+
this.$data.parent.$setNymph(nymph);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async $getUniques(): Promise<string[]> {
|
|
251
|
+
const tilmeld = enforceTilmeld(this);
|
|
252
|
+
const uniques = [`u:${this.$data.groupname}`];
|
|
253
|
+
if (
|
|
254
|
+
!tilmeld.config.emailUsernames &&
|
|
255
|
+
tilmeld.config.userFields.includes('email')
|
|
256
|
+
) {
|
|
257
|
+
uniques.push(`e:${this.$data.email}`);
|
|
258
|
+
}
|
|
259
|
+
if (this.$data.defaultPrimary) {
|
|
260
|
+
uniques.push(`o:default-primary`);
|
|
261
|
+
}
|
|
262
|
+
return uniques;
|
|
263
|
+
}
|
|
264
|
+
|
|
232
265
|
public $getAvatar() {
|
|
233
266
|
if (this.$data.avatar != null && this.$data.avatar !== '') {
|
|
234
267
|
return this.$data.avatar;
|
|
@@ -768,7 +801,11 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
768
801
|
);
|
|
769
802
|
if (currentPrimary != null && !this.$is(currentPrimary)) {
|
|
770
803
|
currentPrimary.defaultPrimary = false;
|
|
771
|
-
if (
|
|
804
|
+
if (
|
|
805
|
+
this.$skipAcWhenSaving
|
|
806
|
+
? !(await currentPrimary.$saveSkipAC())
|
|
807
|
+
: !(await currentPrimary.$save())
|
|
808
|
+
) {
|
|
772
809
|
throw new CouldNotChangeDefaultPrimaryGroupError(
|
|
773
810
|
'Could not change new user primary group from ' +
|
|
774
811
|
`${currentPrimary.groupname}.`,
|
|
@@ -798,23 +835,27 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
798
835
|
|
|
799
836
|
public async $delete() {
|
|
800
837
|
let tilmeld = enforceTilmeld(this);
|
|
801
|
-
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
838
|
+
if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
|
|
802
839
|
throw new BadDataError("You don't have the authority to delete groups.");
|
|
803
840
|
}
|
|
804
841
|
|
|
805
842
|
const transaction = 'tilmeld-delete-' + this.guid;
|
|
806
843
|
const nymph = this.$nymph;
|
|
807
844
|
const tnymph = await nymph.startTransaction(transaction);
|
|
808
|
-
this.$
|
|
845
|
+
this.$setNymph(tnymph);
|
|
809
846
|
tilmeld = enforceTilmeld(this);
|
|
810
847
|
|
|
811
848
|
// Delete descendants.
|
|
812
849
|
const descendants = await this.$getDescendants();
|
|
813
850
|
if (descendants.length) {
|
|
814
851
|
for (let curGroup of descendants) {
|
|
815
|
-
if (
|
|
852
|
+
if (
|
|
853
|
+
this.$skipAcWhenDeleting
|
|
854
|
+
? !(await curGroup.$deleteSkipAC())
|
|
855
|
+
: !(await curGroup.$delete())
|
|
856
|
+
) {
|
|
816
857
|
await tnymph.rollback(transaction);
|
|
817
|
-
this.$nymph
|
|
858
|
+
this.$setNymph(nymph);
|
|
818
859
|
return false;
|
|
819
860
|
}
|
|
820
861
|
}
|
|
@@ -833,9 +874,13 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
833
874
|
);
|
|
834
875
|
for (let user of primaryUsers) {
|
|
835
876
|
delete user.group;
|
|
836
|
-
if (
|
|
877
|
+
if (
|
|
878
|
+
this.$skipAcWhenDeleting
|
|
879
|
+
? !(await user.$saveSkipAC())
|
|
880
|
+
: !(await user.$save())
|
|
881
|
+
) {
|
|
837
882
|
await tnymph.rollback(transaction);
|
|
838
|
-
this.$nymph
|
|
883
|
+
this.$setNymph(nymph);
|
|
839
884
|
return false;
|
|
840
885
|
}
|
|
841
886
|
}
|
|
@@ -853,7 +898,11 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
853
898
|
);
|
|
854
899
|
for (let user of secondaryUsers) {
|
|
855
900
|
user.$delGroup(this);
|
|
856
|
-
if (
|
|
901
|
+
if (
|
|
902
|
+
this.$skipAcWhenDeleting
|
|
903
|
+
? !(await user.$saveSkipAC())
|
|
904
|
+
: !(await user.$save())
|
|
905
|
+
) {
|
|
857
906
|
await tnymph.rollback(transaction);
|
|
858
907
|
return false;
|
|
859
908
|
}
|
|
@@ -866,7 +915,23 @@ export default class Group extends AbleObject<GroupData> {
|
|
|
866
915
|
} else {
|
|
867
916
|
await tnymph.rollback(transaction);
|
|
868
917
|
}
|
|
869
|
-
this.$nymph
|
|
918
|
+
this.$setNymph(nymph);
|
|
870
919
|
return success;
|
|
871
920
|
}
|
|
921
|
+
|
|
922
|
+
/*
|
|
923
|
+
* This should *never* be accessible on the client.
|
|
924
|
+
*/
|
|
925
|
+
async $deleteSkipAC() {
|
|
926
|
+
this.$skipAcWhenDeleting = true;
|
|
927
|
+
return await this.$delete();
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
$tilmeldDeleteSkipAC() {
|
|
931
|
+
if (this.$skipAcWhenDeleting) {
|
|
932
|
+
this.$skipAcWhenDeleting = false;
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
return false;
|
|
936
|
+
}
|
|
872
937
|
}
|
package/src/User.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SQLite3Driver } from '@nymphjs/driver-sqlite3';
|
|
2
|
-
import { Nymph } from '@nymphjs/nymph';
|
|
2
|
+
import { Nymph, EntityUniqueConstraintError } from '@nymphjs/nymph';
|
|
3
3
|
|
|
4
4
|
import Tilmeld from './Tilmeld';
|
|
5
5
|
import defaults from './conf/defaults';
|
|
@@ -58,4 +58,80 @@ describe('User', () => {
|
|
|
58
58
|
defaults.validatorUser({ config: defaults } as Tilmeld, invalidUser);
|
|
59
59
|
}).toThrow('Invalid User: "unknown" is not allowed');
|
|
60
60
|
});
|
|
61
|
+
|
|
62
|
+
it("doesn't allow duplicate usernames", async () => {
|
|
63
|
+
const newUserA = await User.factory();
|
|
64
|
+
|
|
65
|
+
newUserA.username = 'new-user';
|
|
66
|
+
newUserA.email = 'newusera@localhost';
|
|
67
|
+
newUserA.nameFirst = 'New';
|
|
68
|
+
newUserA.nameLast = 'User';
|
|
69
|
+
newUserA.name = 'New User';
|
|
70
|
+
newUserA.$password('password');
|
|
71
|
+
|
|
72
|
+
const newUserB = await User.factory();
|
|
73
|
+
|
|
74
|
+
newUserB.username = 'new-user';
|
|
75
|
+
newUserB.email = 'newuserb@localhost';
|
|
76
|
+
newUserB.nameFirst = 'New';
|
|
77
|
+
newUserB.nameLast = 'User';
|
|
78
|
+
newUserB.name = 'New User';
|
|
79
|
+
newUserB.$password('password');
|
|
80
|
+
|
|
81
|
+
const promises = [newUserA.$saveSkipAC(), newUserB.$saveSkipAC()];
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await Promise.all(promises);
|
|
85
|
+
|
|
86
|
+
throw new Error("Shouldn't get here because of unique constraint.");
|
|
87
|
+
} catch (e: any) {
|
|
88
|
+
expect(e).toBeInstanceOf(EntityUniqueConstraintError);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await Promise.allSettled(promises);
|
|
92
|
+
|
|
93
|
+
for (const user of [newUserA, newUserB]) {
|
|
94
|
+
if (user.guid != null) {
|
|
95
|
+
expect(await user.$deleteSkipAC()).toEqual(true);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("doesn't allow duplicate emails", async () => {
|
|
101
|
+
const newUserA = await User.factory();
|
|
102
|
+
|
|
103
|
+
newUserA.username = 'new-user-a';
|
|
104
|
+
newUserA.email = 'newuser@localhost';
|
|
105
|
+
newUserA.nameFirst = 'New';
|
|
106
|
+
newUserA.nameLast = 'User';
|
|
107
|
+
newUserA.name = 'New User';
|
|
108
|
+
newUserA.$password('password');
|
|
109
|
+
|
|
110
|
+
const newUserB = await User.factory();
|
|
111
|
+
|
|
112
|
+
newUserB.username = 'new-user-b';
|
|
113
|
+
newUserB.email = 'newuser@localhost';
|
|
114
|
+
newUserB.nameFirst = 'New';
|
|
115
|
+
newUserB.nameLast = 'User';
|
|
116
|
+
newUserB.name = 'New User';
|
|
117
|
+
newUserB.$password('password');
|
|
118
|
+
|
|
119
|
+
const promises = [newUserA.$saveSkipAC(), newUserB.$saveSkipAC()];
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
await Promise.all(promises);
|
|
123
|
+
|
|
124
|
+
throw new Error("Shouldn't get here because of unique constraint.");
|
|
125
|
+
} catch (e: any) {
|
|
126
|
+
expect(e).toBeInstanceOf(EntityUniqueConstraintError);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await Promise.allSettled(promises);
|
|
130
|
+
|
|
131
|
+
for (const user of [newUserA, newUserB]) {
|
|
132
|
+
if (user.guid != null) {
|
|
133
|
+
expect(await user.$deleteSkipAC()).toEqual(true);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
61
137
|
});
|