mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-30 10:36:35 +02:00
Server: Set a timestamp when disabling a user
So that it can be used later on to decide if user data should be automatically deleted or not.
This commit is contained in:
parent
46202beb9b
commit
2f7ab7e92e
Binary file not shown.
@ -0,0 +1,54 @@
|
||||
import { Knex } from 'knex';
|
||||
import { DbConnection } from '../db';
|
||||
|
||||
// It's assumed that the input user IDs are disabled.
|
||||
// The disabled_time will be set to the first flag created_time
|
||||
export const setUserAccountDisabledTimes = async (db: DbConnection, userIds: string[]) => {
|
||||
// FailedPaymentFinal = 2,
|
||||
// SubscriptionCancelled = 5,
|
||||
// ManuallyDisabled = 6,
|
||||
// UserDeletionInProgress = 7,
|
||||
|
||||
interface UserFlag {
|
||||
user_id: string;
|
||||
created_time: number;
|
||||
}
|
||||
|
||||
const flags: UserFlag[] = await db('user_flags')
|
||||
.select(['user_id', 'created_time'])
|
||||
.whereIn('user_id', userIds)
|
||||
.whereIn('type', [2, 5, 6, 7])
|
||||
.orderBy('created_time', 'asc');
|
||||
|
||||
for (const userId of userIds) {
|
||||
const flag = flags.find(f => f.user_id === userId);
|
||||
|
||||
if (!flag) {
|
||||
console.warn(`Found a disabled account without an associated flag. Setting disabled timestamp to current time: ${userId}`);
|
||||
}
|
||||
|
||||
await db('users')
|
||||
.update({ disabled_time: flag ? flag.created_time : Date.now() })
|
||||
.where('id', '=', userId);
|
||||
}
|
||||
};
|
||||
|
||||
export const disabledUserIds = async (db: DbConnection): Promise<string[]> => {
|
||||
const users = await db('users').select(['id']).where('enabled', '=', 0);
|
||||
return users.map(u => u.id);
|
||||
};
|
||||
|
||||
export async function up(db: DbConnection): Promise<any> {
|
||||
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
|
||||
table.bigInteger('disabled_time').defaultTo(0).notNullable();
|
||||
});
|
||||
|
||||
const userIds = await disabledUserIds(db);
|
||||
await setUserAccountDisabledTimes(db, userIds);
|
||||
}
|
||||
|
||||
export async function down(db: DbConnection): Promise<any> {
|
||||
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
|
||||
table.dropColumn('disabled_time');
|
||||
});
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import { UserFlagType } from '../../services/database/types';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, createUser, models, db } from '../../utils/testing/testUtils';
|
||||
import { disabledUserIds, setUserAccountDisabledTimes } from '../20220131185922_account_disabled_timestamp';
|
||||
|
||||
describe('20220131185922_account_disabled_timestamp', function() {
|
||||
|
||||
beforeAll(async () => {
|
||||
await beforeAllDb('20220131185922_account_disabled_timestamp');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await afterAllTests();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEachDb();
|
||||
});
|
||||
|
||||
test('should set the user account disabled time', async function() {
|
||||
const user1 = await createUser(1);
|
||||
const user2 = await createUser(2);
|
||||
const user3 = await createUser(3);
|
||||
const user4 = await createUser(4);
|
||||
|
||||
jest.useFakeTimers('modern');
|
||||
|
||||
// -------------------------------------------------
|
||||
// User 1
|
||||
// -------------------------------------------------
|
||||
|
||||
const t0 = new Date('2021-12-14').getTime();
|
||||
jest.setSystemTime(t0);
|
||||
|
||||
await models().userFlag().add(user1.id, UserFlagType.AccountOverLimit);
|
||||
|
||||
// -------------------------------------------------
|
||||
// User 2
|
||||
// -------------------------------------------------
|
||||
|
||||
const t1 = new Date('2021-12-15').getTime();
|
||||
jest.setSystemTime(t1);
|
||||
|
||||
await models().userFlag().add(user2.id, UserFlagType.FailedPaymentFinal);
|
||||
|
||||
const t2 = new Date('2021-12-16').getTime();
|
||||
jest.setSystemTime(t2);
|
||||
|
||||
await models().userFlag().add(user2.id, UserFlagType.ManuallyDisabled);
|
||||
|
||||
// -------------------------------------------------
|
||||
// User 3
|
||||
// -------------------------------------------------
|
||||
|
||||
const t3 = new Date('2021-12-17').getTime();
|
||||
jest.setSystemTime(t3);
|
||||
|
||||
await models().userFlag().add(user3.id, UserFlagType.SubscriptionCancelled);
|
||||
|
||||
const userIds = await disabledUserIds(db());
|
||||
expect(userIds.sort()).toEqual([user2.id, user3.id].sort());
|
||||
|
||||
await setUserAccountDisabledTimes(db(), userIds);
|
||||
|
||||
expect((await models().user().load(user1.id)).disabled_time).toBe(0);
|
||||
expect((await models().user().load(user2.id)).disabled_time).toBe(t1);
|
||||
expect((await models().user().load(user3.id)).disabled_time).toBe(t3);
|
||||
expect((await models().user().load(user4.id)).disabled_time).toBe(0);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
});
|
@ -39,4 +39,17 @@ describe('UserFlagModel', function() {
|
||||
expect(flag.id).not.toBe(differentFlag.id);
|
||||
});
|
||||
|
||||
test('should set the timestamp when disabling an account', async function() {
|
||||
const { user } = await createUserAndSession(1);
|
||||
|
||||
const beforeTime = Date.now();
|
||||
await models().userFlag().add(user.id, UserFlagType.FailedPaymentFinal);
|
||||
|
||||
expect((await models().user().load(user.id)).disabled_time).toBeGreaterThanOrEqual(beforeTime);
|
||||
|
||||
await models().userFlag().remove(user.id, UserFlagType.FailedPaymentFinal);
|
||||
|
||||
expect((await models().user().load(user.id)).disabled_time).toBe(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -138,6 +138,10 @@ export default class UserFlagModels extends BaseModel<UserFlag> {
|
||||
newProps.enabled = 0;
|
||||
}
|
||||
|
||||
if (user.enabled !== newProps.enabled) {
|
||||
newProps.disabled_time = !newProps.enabled ? Date.now() : 0;
|
||||
}
|
||||
|
||||
if (user.can_upload !== newProps.can_upload || user.enabled !== newProps.enabled) {
|
||||
await this.models().user().save({
|
||||
id: userId,
|
||||
|
@ -186,20 +186,6 @@ export interface Change extends WithDates, WithUuid {
|
||||
user_id?: Uuid;
|
||||
}
|
||||
|
||||
export interface Email extends WithDates {
|
||||
id?: number;
|
||||
recipient_name?: string;
|
||||
recipient_email?: string;
|
||||
recipient_id?: Uuid;
|
||||
sender_id?: EmailSender;
|
||||
subject?: string;
|
||||
body?: string;
|
||||
sent_time?: number;
|
||||
sent_success?: number;
|
||||
error?: string;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export interface Token extends WithDates {
|
||||
id?: number;
|
||||
value?: string;
|
||||
@ -233,6 +219,7 @@ export interface User extends WithDates, WithUuid {
|
||||
max_total_item_size?: number | null;
|
||||
total_item_size?: number;
|
||||
enabled?: number;
|
||||
disabled_time?: number;
|
||||
}
|
||||
|
||||
export interface UserFlag extends WithDates {
|
||||
@ -282,6 +269,40 @@ export interface UserDeletion extends WithDates {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface Organization {
|
||||
id?: Uuid;
|
||||
name?: string;
|
||||
owner_id?: Uuid;
|
||||
max_users?: number;
|
||||
updated_time?: string;
|
||||
created_time?: string;
|
||||
}
|
||||
|
||||
export interface OrganizationUser {
|
||||
id?: Uuid;
|
||||
organization_id?: Uuid;
|
||||
user_id?: Uuid;
|
||||
invitation_email?: string;
|
||||
invitation_status?: number;
|
||||
is_admin?: number;
|
||||
updated_time?: string;
|
||||
created_time?: string;
|
||||
}
|
||||
|
||||
export interface Email extends WithDates {
|
||||
id?: number;
|
||||
recipient_name?: string;
|
||||
recipient_email?: string;
|
||||
recipient_id?: Uuid;
|
||||
sender_id?: EmailSender;
|
||||
subject?: string;
|
||||
body?: string;
|
||||
sent_time?: number;
|
||||
sent_success?: number;
|
||||
error?: string;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export const databaseSchema: DatabaseTables = {
|
||||
sessions: {
|
||||
id: { type: 'string' },
|
||||
@ -374,21 +395,6 @@ export const databaseSchema: DatabaseTables = {
|
||||
previous_item: { type: 'string' },
|
||||
user_id: { type: 'string' },
|
||||
},
|
||||
emails: {
|
||||
id: { type: 'number' },
|
||||
recipient_name: { type: 'string' },
|
||||
recipient_email: { type: 'string' },
|
||||
recipient_id: { type: 'string' },
|
||||
sender_id: { type: 'number' },
|
||||
subject: { type: 'string' },
|
||||
body: { type: 'string' },
|
||||
sent_time: { type: 'string' },
|
||||
sent_success: { type: 'number' },
|
||||
error: { type: 'string' },
|
||||
updated_time: { type: 'string' },
|
||||
created_time: { type: 'string' },
|
||||
key: { type: 'string' },
|
||||
},
|
||||
tokens: {
|
||||
id: { type: 'number' },
|
||||
value: { type: 'string' },
|
||||
@ -425,6 +431,7 @@ export const databaseSchema: DatabaseTables = {
|
||||
max_total_item_size: { type: 'string' },
|
||||
total_item_size: { type: 'string' },
|
||||
enabled: { type: 'number' },
|
||||
disabled_time: { type: 'string' },
|
||||
},
|
||||
user_flags: {
|
||||
id: { type: 'number' },
|
||||
@ -476,5 +483,38 @@ export const databaseSchema: DatabaseTables = {
|
||||
updated_time: { type: 'string' },
|
||||
created_time: { type: 'string' },
|
||||
},
|
||||
organizations: {
|
||||
id: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
owner_id: { type: 'string' },
|
||||
max_users: { type: 'number' },
|
||||
updated_time: { type: 'string' },
|
||||
created_time: { type: 'string' },
|
||||
},
|
||||
organization_users: {
|
||||
id: { type: 'string' },
|
||||
organization_id: { type: 'string' },
|
||||
user_id: { type: 'string' },
|
||||
invitation_email: { type: 'string' },
|
||||
invitation_status: { type: 'number' },
|
||||
is_admin: { type: 'number' },
|
||||
updated_time: { type: 'string' },
|
||||
created_time: { type: 'string' },
|
||||
},
|
||||
emails: {
|
||||
id: { type: 'number' },
|
||||
recipient_name: { type: 'string' },
|
||||
recipient_email: { type: 'string' },
|
||||
recipient_id: { type: 'string' },
|
||||
sender_id: { type: 'number' },
|
||||
subject: { type: 'string' },
|
||||
body: { type: 'string' },
|
||||
sent_time: { type: 'string' },
|
||||
sent_success: { type: 'number' },
|
||||
error: { type: 'string' },
|
||||
updated_time: { type: 'string' },
|
||||
created_time: { type: 'string' },
|
||||
key: { type: 'string' },
|
||||
},
|
||||
};
|
||||
// AUTO-GENERATED-TYPES
|
||||
|
@ -62,6 +62,7 @@ const propertyTypes: Record<string, string> = {
|
||||
'user_deletions.start_time': 'number',
|
||||
'user_deletions.end_time': 'number',
|
||||
'user_deletions.scheduled_time': 'number',
|
||||
'users.disabled_time': 'number',
|
||||
};
|
||||
|
||||
function insertContentIntoFile(filePath: string, markerOpen: string, markerClose: string, contentToInsert: string): void {
|
||||
|
Loading…
Reference in New Issue
Block a user