mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Split AccountStorage and ForgotPasswordStorage (expiring now)
This commit is contained in:
parent
90a6460c8d
commit
d067165b68
@ -8,7 +8,21 @@
|
|||||||
"saltRounds": 10,
|
"saltRounds": 10,
|
||||||
"storage": {
|
"storage": {
|
||||||
"@id": "urn:solid-server:default:AccountStorage"
|
"@id": "urn:solid-server:default:AccountStorage"
|
||||||
|
},
|
||||||
|
"forgotPasswordStorage": {
|
||||||
|
"@id": "urn:solid-server:default:ExpiringForgotPasswordStorage"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Stores expiring data. This class has a `finalize` function that needs to be called after stopping the server.",
|
||||||
|
"@id": "urn:solid-server:default:ExpiringForgotPasswordStorage",
|
||||||
|
"@type": "WrappedExpiringStorage",
|
||||||
|
"source": { "@id": "urn:solid-server:default:ForgotPasswordStorage" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Makes sure the expiring storage cleanup timer is stopped when the application needs to stop.",
|
||||||
|
"@id": "urn:solid-server:default:Finalizer",
|
||||||
|
"ParallelFinalizer:_finalizers": [ { "@id": "urn:solid-server:default:ExpiringForgotPasswordStorage" } ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,11 @@
|
|||||||
"comment": "Storage used by setup components.",
|
"comment": "Storage used by setup components.",
|
||||||
"@id": "urn:solid-server:default:SetupStorage",
|
"@id": "urn:solid-server:default:SetupStorage",
|
||||||
"@type": "MemoryMapStorage"
|
"@type": "MemoryMapStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Storage used for ForgotPassword records",
|
||||||
|
"@id": "urn:solid-server:default:ForgotPasswordStorage",
|
||||||
|
"@type":"MemoryMapStorage"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,14 @@
|
|||||||
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
||||||
"container": "/.internal/accounts/"
|
"container": "/.internal/accounts/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"comment": "Storage used for ForgotPassword records",
|
||||||
|
"@id": "urn:solid-server:default:ForgotPasswordStorage",
|
||||||
|
"@type":"JsonResourceStorage",
|
||||||
|
"source": { "@id": "urn:solid-server:default:ResourceStore" },
|
||||||
|
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
||||||
|
"container": "/.internal/forgot-password/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"comment": "Storage used by setup components.",
|
"comment": "Storage used by setup components.",
|
||||||
"@id": "urn:solid-server:default:SetupStorage",
|
"@id": "urn:solid-server:default:SetupStorage",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { hash, compare } from 'bcrypt';
|
import { hash, compare } from 'bcrypt';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
import type { ExpiringStorage } from '../../../../storage/keyvalue/ExpiringStorage';
|
||||||
import type { KeyValueStorage } from '../../../../storage/keyvalue/KeyValueStorage';
|
import type { KeyValueStorage } from '../../../../storage/keyvalue/KeyValueStorage';
|
||||||
import type { AccountSettings, AccountStore } from './AccountStore';
|
import type { AccountSettings, AccountStore } from './AccountStore';
|
||||||
|
|
||||||
@ -26,15 +27,25 @@ export interface ForgotPasswordPayload {
|
|||||||
export type EmailPasswordData = AccountPayload | ForgotPasswordPayload | AccountSettings;
|
export type EmailPasswordData = AccountPayload | ForgotPasswordPayload | AccountSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A EmailPasswordStore that uses a KeyValueStorage
|
* A EmailPasswordStore that uses a KeyValueStorage to persist its information and an
|
||||||
* to persist its information.
|
* ExpiringStorage to persist ForgotPassword records.
|
||||||
|
*
|
||||||
|
* `forgotPasswordExpiration` parameter is how long the ForgotPassword record should be
|
||||||
|
* stored in minutes. *(defaults to 15 minutes)*
|
||||||
*/
|
*/
|
||||||
export class BaseAccountStore implements AccountStore {
|
export class BaseAccountStore implements AccountStore {
|
||||||
private readonly storage: KeyValueStorage<string, EmailPasswordData>;
|
private readonly storage: KeyValueStorage<string, EmailPasswordData>;
|
||||||
|
private readonly forgotPasswordStorage: ExpiringStorage<string, EmailPasswordData>;
|
||||||
private readonly saltRounds: number;
|
private readonly saltRounds: number;
|
||||||
|
private readonly forgotPasswordExpiration: number;
|
||||||
|
|
||||||
public constructor(storage: KeyValueStorage<string, EmailPasswordData>, saltRounds: number) {
|
public constructor(storage: KeyValueStorage<string, EmailPasswordData>,
|
||||||
|
forgotPasswordStorage: ExpiringStorage<string, EmailPasswordData>,
|
||||||
|
saltRounds: number,
|
||||||
|
forgotPasswordExpiration = 15) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
this.forgotPasswordStorage = forgotPasswordStorage;
|
||||||
|
this.forgotPasswordExpiration = forgotPasswordExpiration * 60 * 1000;
|
||||||
this.saltRounds = saltRounds;
|
this.saltRounds = saltRounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,20 +141,21 @@ export class BaseAccountStore implements AccountStore {
|
|||||||
public async generateForgotPasswordRecord(email: string): Promise<string> {
|
public async generateForgotPasswordRecord(email: string): Promise<string> {
|
||||||
const recordId = v4();
|
const recordId = v4();
|
||||||
await this.getAccountPayload(email, true);
|
await this.getAccountPayload(email, true);
|
||||||
await this.storage.set(
|
await this.forgotPasswordStorage.set(
|
||||||
this.getForgotPasswordRecordResourceIdentifier(recordId),
|
this.getForgotPasswordRecordResourceIdentifier(recordId),
|
||||||
{ recordId, email },
|
{ recordId, email },
|
||||||
|
this.forgotPasswordExpiration,
|
||||||
);
|
);
|
||||||
return recordId;
|
return recordId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getForgotPasswordRecord(recordId: string): Promise<string | undefined> {
|
public async getForgotPasswordRecord(recordId: string): Promise<string | undefined> {
|
||||||
const identifier = this.getForgotPasswordRecordResourceIdentifier(recordId);
|
const identifier = this.getForgotPasswordRecordResourceIdentifier(recordId);
|
||||||
const forgotPasswordRecord = await this.storage.get(identifier) as ForgotPasswordPayload | undefined;
|
const forgotPasswordRecord = await this.forgotPasswordStorage.get(identifier) as ForgotPasswordPayload | undefined;
|
||||||
return forgotPasswordRecord?.email;
|
return forgotPasswordRecord?.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteForgotPasswordRecord(recordId: string): Promise<void> {
|
public async deleteForgotPasswordRecord(recordId: string): Promise<void> {
|
||||||
await this.storage.delete(this.getForgotPasswordRecordResourceIdentifier(recordId));
|
await this.forgotPasswordStorage.delete(this.getForgotPasswordRecordResourceIdentifier(recordId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@ import type {
|
|||||||
EmailPasswordData,
|
EmailPasswordData,
|
||||||
} from '../../../../../../src/identity/interaction/email-password/storage/BaseAccountStore';
|
} from '../../../../../../src/identity/interaction/email-password/storage/BaseAccountStore';
|
||||||
import { BaseAccountStore } from '../../../../../../src/identity/interaction/email-password/storage/BaseAccountStore';
|
import { BaseAccountStore } from '../../../../../../src/identity/interaction/email-password/storage/BaseAccountStore';
|
||||||
|
import type { ExpiringStorage } from '../../../../../../src/storage/keyvalue/ExpiringStorage';
|
||||||
import type { KeyValueStorage } from '../../../../../../src/storage/keyvalue/KeyValueStorage';
|
import type { KeyValueStorage } from '../../../../../../src/storage/keyvalue/KeyValueStorage';
|
||||||
|
|
||||||
describe('A BaseAccountStore', (): void => {
|
describe('A BaseAccountStore', (): void => {
|
||||||
let storage: KeyValueStorage<string, EmailPasswordData>;
|
let storage: KeyValueStorage<string, EmailPasswordData>;
|
||||||
|
let forgotPasswordStorage: ExpiringStorage<string, EmailPasswordData>;
|
||||||
const saltRounds = 11;
|
const saltRounds = 11;
|
||||||
let store: BaseAccountStore;
|
let store: BaseAccountStore;
|
||||||
const email = 'test@test.com';
|
const email = 'test@test.com';
|
||||||
@ -22,7 +24,13 @@ describe('A BaseAccountStore', (): void => {
|
|||||||
delete: jest.fn((id: string): any => map.delete(id)),
|
delete: jest.fn((id: string): any => map.delete(id)),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
store = new BaseAccountStore(storage, saltRounds);
|
forgotPasswordStorage = {
|
||||||
|
get: jest.fn((id: string): any => map.get(id)),
|
||||||
|
set: jest.fn((id: string, value: any): any => map.set(id, value)),
|
||||||
|
delete: jest.fn((id: string): any => map.delete(id)),
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
store = new BaseAccountStore(storage, forgotPasswordStorage, saltRounds);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can create accounts.', async(): Promise<void> => {
|
it('can create accounts.', async(): Promise<void> => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user