зеркало из https://github.com/mozilla/fxa.git
feat(phone): Add `unconfirmed` to RecoveryPhoneManager
This commit is contained in:
Родитель
13f7bc8202
Коммит
d758f4939e
|
@ -11,6 +11,12 @@ describe('RecoveryPhoneManager', () => {
|
|||
let recoveryPhoneManager: RecoveryPhoneManager;
|
||||
let db: AccountDatabase;
|
||||
|
||||
const mockRedis = {
|
||||
set: jest.fn(),
|
||||
get: jest.fn(),
|
||||
del: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await testAccountDatabaseSetup(['accounts', 'recoveryPhones']);
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
|
@ -20,6 +26,10 @@ describe('RecoveryPhoneManager', () => {
|
|||
provide: AccountDbProvider,
|
||||
useValue: db,
|
||||
},
|
||||
{
|
||||
provide: 'Redis',
|
||||
useValue: mockRedis,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
@ -76,4 +86,49 @@ describe('RecoveryPhoneManager', () => {
|
|||
recoveryPhoneManager.registerPhoneNumber(uid.toString('hex'), phoneNumber)
|
||||
).rejects.toThrow('Database error');
|
||||
});
|
||||
|
||||
it('should store unconfirmed phone number data in Redis', async () => {
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
const { uid, phoneNumber } = mockPhone;
|
||||
const code = '123456';
|
||||
const isSetup = true;
|
||||
const lookupData = { foo: 'bar' };
|
||||
|
||||
await recoveryPhoneManager.storeUnconfirmed(
|
||||
uid.toString('hex'),
|
||||
code,
|
||||
phoneNumber,
|
||||
isSetup,
|
||||
lookupData
|
||||
);
|
||||
|
||||
const expectedData = JSON.stringify({
|
||||
phoneNumber,
|
||||
isSetup,
|
||||
lookupData: JSON.stringify(lookupData),
|
||||
});
|
||||
const redisKey = `sms-attempt:${uid.toString('hex')}:${code}`;
|
||||
|
||||
expect(mockRedis.set).toHaveBeenCalledWith(
|
||||
redisKey,
|
||||
expectedData,
|
||||
'EX',
|
||||
600
|
||||
);
|
||||
});
|
||||
|
||||
it('should return null if no unconfirmed phone number data is found in Redis', async () => {
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
const { uid } = mockPhone;
|
||||
const code = '123456';
|
||||
|
||||
mockRedis.get.mockResolvedValue(null);
|
||||
|
||||
const result = await recoveryPhoneManager.getUnconfirmed(
|
||||
uid.toString('hex'),
|
||||
code
|
||||
);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,11 +12,17 @@ import {
|
|||
RecoveryNumberAlreadyExistsError,
|
||||
RecoveryNumberInvalidFormatError,
|
||||
} from './recovery-phone.errors';
|
||||
import { Redis } from 'ioredis';
|
||||
|
||||
const RECORD_EXPIRATION_SECONDS = 10 * 60;
|
||||
|
||||
@Injectable()
|
||||
export class RecoveryPhoneManager {
|
||||
private readonly redisPrefix = 'sms-attempt';
|
||||
|
||||
constructor(
|
||||
@Inject(AccountDbProvider) private readonly db: AccountDatabase
|
||||
@Inject(AccountDbProvider) private readonly db: AccountDatabase,
|
||||
@Inject('Redis') private readonly redisClient: Redis
|
||||
) {}
|
||||
|
||||
private isE164Format(phoneNumber: string) {
|
||||
|
@ -57,4 +63,60 @@ export class RecoveryPhoneManager {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store phone number data and SMS code for a user.
|
||||
*
|
||||
* @param uid The user's unique identifier
|
||||
* @param code The SMS code to associate with this UID
|
||||
* @param phoneNumber The phone number to store
|
||||
* @param isSetup Flag indicating if this SMS is to set up a number or verify an existing one
|
||||
* @param lookupData Optional lookup data for the phone number
|
||||
*/
|
||||
async storeUnconfirmed(
|
||||
uid: string,
|
||||
code: string,
|
||||
phoneNumber: string,
|
||||
isSetup: boolean,
|
||||
lookupData?: Record<string, any>
|
||||
): Promise<void> {
|
||||
const redisKey = `${this.redisPrefix}:${uid}:${code}`;
|
||||
const data = {
|
||||
phoneNumber,
|
||||
isSetup,
|
||||
lookupData: lookupData ? JSON.stringify(lookupData) : null,
|
||||
};
|
||||
|
||||
await this.redisClient.set(
|
||||
redisKey,
|
||||
JSON.stringify(data),
|
||||
'EX',
|
||||
RECORD_EXPIRATION_SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve phone number data for a user using uid and sms code.
|
||||
*
|
||||
* @param uid The user's unique identifier
|
||||
* @param code The SMS code associated with this user
|
||||
* @returns The stored phone number data if found, or null if not found
|
||||
*/
|
||||
async getUnconfirmed(
|
||||
uid: string,
|
||||
code: string
|
||||
): Promise<{
|
||||
phoneNumber: string;
|
||||
isSetup: boolean;
|
||||
lookupData: Record<string, any> | null;
|
||||
} | null> {
|
||||
const redisKey = `${this.redisPrefix}:${uid}:${code}`;
|
||||
const data = await this.redisClient.get(redisKey);
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче