Skip to content

Commit

Permalink
fix: Split AccountStorage and ForgotPasswordStorage (expiring now)
Browse files Browse the repository at this point in the history
  • Loading branch information
Falx committed Feb 9, 2022
1 parent 90a6460 commit d067165
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 7 deletions.
14 changes: 14 additions & 0 deletions config/identity/handler/account-store/default.json
Expand Up @@ -8,7 +8,21 @@
"saltRounds": 10,
"storage": {
"@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" } ]
}
]
}
5 changes: 5 additions & 0 deletions config/storage/key-value/memory.json
Expand Up @@ -33,6 +33,11 @@
"comment": "Storage used by setup components.",
"@id": "urn:solid-server:default:SetupStorage",
"@type": "MemoryMapStorage"
},
{
"comment": "Storage used for ForgotPassword records",
"@id": "urn:solid-server:default:ForgotPasswordStorage",
"@type":"MemoryMapStorage"
}
]
}
8 changes: 8 additions & 0 deletions config/storage/key-value/resource-store.json
Expand Up @@ -47,6 +47,14 @@
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"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.",
"@id": "urn:solid-server:default:SetupStorage",
Expand Down
@@ -1,6 +1,7 @@
import assert from 'assert';
import { hash, compare } from 'bcrypt';
import { v4 } from 'uuid';
import type { ExpiringStorage } from '../../../../storage/keyvalue/ExpiringStorage';
import type { KeyValueStorage } from '../../../../storage/keyvalue/KeyValueStorage';
import type { AccountSettings, AccountStore } from './AccountStore';

Expand All @@ -26,15 +27,25 @@ export interface ForgotPasswordPayload {
export type EmailPasswordData = AccountPayload | ForgotPasswordPayload | AccountSettings;

/**
* A EmailPasswordStore that uses a KeyValueStorage
* to persist its information.
* A EmailPasswordStore that uses a KeyValueStorage to persist its information and an
* 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 {
private readonly storage: KeyValueStorage<string, EmailPasswordData>;
private readonly forgotPasswordStorage: ExpiringStorage<string, EmailPasswordData>;
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.forgotPasswordStorage = forgotPasswordStorage;
this.forgotPasswordExpiration = forgotPasswordExpiration * 60 * 1000;
this.saltRounds = saltRounds;
}

Expand Down Expand Up @@ -130,20 +141,21 @@ export class BaseAccountStore implements AccountStore {
public async generateForgotPasswordRecord(email: string): Promise<string> {
const recordId = v4();
await this.getAccountPayload(email, true);
await this.storage.set(
await this.forgotPasswordStorage.set(
this.getForgotPasswordRecordResourceIdentifier(recordId),
{ recordId, email },
this.forgotPasswordExpiration,
);
return recordId;
}

public async getForgotPasswordRecord(recordId: string): Promise<string | undefined> {
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;
}

public async deleteForgotPasswordRecord(recordId: string): Promise<void> {
await this.storage.delete(this.getForgotPasswordRecordResourceIdentifier(recordId));
await this.forgotPasswordStorage.delete(this.getForgotPasswordRecordResourceIdentifier(recordId));
}
}
Expand Up @@ -3,10 +3,12 @@ import type {
EmailPasswordData,
} 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';

describe('A BaseAccountStore', (): void => {
let storage: KeyValueStorage<string, EmailPasswordData>;
let forgotPasswordStorage: ExpiringStorage<string, EmailPasswordData>;
const saltRounds = 11;
let store: BaseAccountStore;
const email = 'test@test.com';
Expand All @@ -22,7 +24,13 @@ describe('A BaseAccountStore', (): void => {
delete: jest.fn((id: string): any => map.delete(id)),
} 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> => {
Expand Down

0 comments on commit d067165

Please sign in to comment.