Skip to content

Commit efd4f2a

Browse files
Merge pull request #527 from PermanentOrg/PER-9985-entering-2fa-incorrect-code-works
Per 9985 entering 2fa incorrect code works
2 parents 2342353 + 8dece3d commit efd4f2a

File tree

5 files changed

+106
-18
lines changed

5 files changed

+106
-18
lines changed

src/app/core/components/two-factor-auth/two-factor-auth.component.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe('TwoFactorAuthComponent', () => {
9393
const event = {
9494
preventDefault: () => {},
9595
};
96-
instance.sendCode(event);
96+
await instance.sendCode(event);
9797

9898
expect(instance.codeSent).toBe(true);
9999
});
@@ -170,4 +170,30 @@ describe('TwoFactorAuthComponent', () => {
170170

171171
expect(codeContainer.length).toBe(0);
172172
});
173+
174+
it('should retrieve all the methods after a method has been deleted', async () => {
175+
const { instance } = await shallow.render();
176+
177+
instance.methods = [
178+
{ methodId: 'email', method: 'email', value: 'janedoe@example.com' },
179+
];
180+
instance.selectedMethodToDelete = {
181+
methodId: 'email',
182+
method: 'email',
183+
value: 'janedoe@example.com',
184+
};
185+
186+
spyOn(mockApiService.idpuser, 'disableTwoFactor').and.returnValue(
187+
Promise.resolve(),
188+
);
189+
spyOn(mockApiService.idpuser, 'getTwoFactorMethods').and.returnValue(
190+
Promise.resolve([
191+
{ methodId: 'sms', method: 'sms', value: '(123) 456-7890' },
192+
]),
193+
);
194+
195+
await instance.submitRemoveMethod();
196+
197+
expect(mockApiService.idpuser.getTwoFactorMethods).toHaveBeenCalled();
198+
});
173199
});

src/app/core/components/two-factor-auth/two-factor-auth.component.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
UntypedFormGroup,
66
Validators,
77
} from '@angular/forms';
8-
import { AccountService } from '@shared/services/account/account.service';
98
import { ApiService } from '@shared/services/api/api.service';
9+
import { MessageService } from '@shared/services/message/message.service';
1010

1111
interface Method {
1212
methodId: string;
@@ -35,6 +35,7 @@ export class TwoFactorAuthComponent implements OnInit {
3535
constructor(
3636
private fb: UntypedFormBuilder,
3737
private api: ApiService,
38+
private message: MessageService,
3839
) {
3940
this.form = fb.group({
4041
code: ['', Validators.required],
@@ -115,7 +116,7 @@ export class TwoFactorAuthComponent implements OnInit {
115116
this.selectedMethodToDelete.methodId,
116117
);
117118
} else {
118-
this.api.idpuser.sendEnableCode(
119+
await this.api.idpuser.sendEnableCode(
119120
this.method,
120121
this.form.get('contactInfo').value,
121122
);
@@ -171,9 +172,7 @@ export class TwoFactorAuthComponent implements OnInit {
171172
);
172173
} catch (error) {
173174
} finally {
174-
this.methods = this.methods.filter(
175-
(m) => m.methodId !== this.selectedMethodToDelete.methodId,
176-
);
175+
this.methods = await this.api.idpuser.getTwoFactorMethods();
177176
this.selectedMethodToDelete = null;
178177
this.codeSent = false;
179178
this.method = null;

src/app/shared/services/api/idpuser.repo.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,55 @@ export class IdPuser extends BaseRepo {
1414

1515
public sendEnableCode(method: string, value: string) {
1616
return firstValueFrom(
17-
this.httpV2.post('/v2/idpuser/send-enable-code', { method, value }),
17+
this.httpV2.post(
18+
'/v2/idpuser/send-enable-code',
19+
{ method, value },
20+
undefined,
21+
{
22+
responseType: 'text',
23+
},
24+
),
1825
);
1926
}
2027

2128
public enableTwoFactor(method: string, value: string, code: string) {
2229
return firstValueFrom(
23-
this.httpV2.post('/v2/idpuser/enable-two-factor', {
24-
method,
25-
value,
26-
code,
27-
}),
30+
this.httpV2.post(
31+
'/v2/idpuser/enable-two-factor',
32+
{
33+
method,
34+
value,
35+
code,
36+
},
37+
undefined,
38+
{
39+
responseType: 'text',
40+
},
41+
),
2842
);
2943
}
3044

3145
public sendDisableCode(methodId: string) {
3246
return firstValueFrom(
33-
this.httpV2.post('/v2/idpuser/send-disable-code', { methodId }),
47+
this.httpV2.post(
48+
'/v2/idpuser/send-disable-code',
49+
{ methodId },
50+
undefined,
51+
{ responseType: 'text' },
52+
),
3453
);
3554
}
3655

3756
public disableTwoFactor(methodId: string, code: string) {
3857
return firstValueFrom(
39-
this.httpV2.post('/v2/idpuser/disable-two-factor', { code, methodId }),
58+
this.httpV2.post(
59+
'/v2/idpuser/disable-two-factor',
60+
{ code, methodId },
61+
undefined,
62+
{
63+
responseType: 'text',
64+
},
65+
),
4066
);
4167
}
4268
}

src/app/shared/services/http-v2/http-v2.service.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,25 @@ describe('HttpV2Service', () => {
284284
'https://local.permanent.org/api/v2/health',
285285
);
286286
});
287+
288+
it('should correctly handle responseType: text', (done) => {
289+
service
290+
.post('/api/v2/health', {}, undefined, {
291+
responseType: 'text',
292+
})
293+
.toPromise()
294+
.then((response) => {
295+
expect(typeof response[0]).toBe('string');
296+
expect(response[0]).toBe('OK');
297+
done();
298+
})
299+
.catch(done.fail);
300+
301+
const request = httpTestingController.expectOne(apiUrl('/api/v2/health'));
302+
303+
expect(request.request.method).toBe('POST');
304+
expect(request.request.headers.get('Request-Version')).toBe('2');
305+
306+
request.flush('OK', { status: 200, statusText: 'OK' });
307+
});
287308
});

src/app/shared/services/http-v2/http-v2.service.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@ const AUTH_KEY = 'AUTH_TOKEN';
1313

1414
type HttpMethod = 'post' | 'get' | 'put' | 'delete';
1515
type ResponseClass<T> = new (data: any) => T;
16+
type ResponseType = 'json' | 'text';
1617

1718
interface RequestOptions {
1819
csrf?: boolean;
1920
authToken?: boolean;
2021
useStelaDomain?: boolean;
22+
responseType?: ResponseType;
2123
}
2224

2325
const defaultOptions: RequestOptions = {
2426
csrf: false,
2527
authToken: true,
2628
useStelaDomain: true,
29+
responseType: 'json',
2730
};
2831

2932
export function getFirst<T>(observable: Observable<T[]>): Observable<T> {
@@ -187,21 +190,29 @@ export class HttpV2Service {
187190
method: HttpMethod,
188191
options: RequestOptions,
189192
): Observable<unknown> {
193+
const requestOptions: Object = {
194+
...this.getHeaders(options),
195+
responseType: options.responseType,
196+
};
190197
if (method === 'put') {
191-
return this.http.put(url, data, this.getHeaders(options));
198+
return this.http.put(url, data, requestOptions);
192199
}
193-
return this.http.post(url, data, this.getHeaders(options));
200+
return this.http.post(url, data, requestOptions);
194201
}
195202

196203
protected getObservableWithNoBody(
197204
url: string,
198205
method: HttpMethod,
199206
options: RequestOptions,
200207
): Observable<unknown> {
208+
const requestOptions: Object = {
209+
...this.getHeaders(options),
210+
responseType: options.responseType,
211+
};
201212
if (method === 'delete') {
202-
return this.http.delete(url, this.getHeaders(options));
213+
return this.http.delete(url, requestOptions);
203214
}
204-
return this.http.get(url, this.getHeaders(options));
215+
return this.http.get(url, requestOptions);
205216
}
206217

207218
protected getObservable(
@@ -233,8 +244,13 @@ export class HttpV2Service {
233244
options: RequestOptions = defaultOptions,
234245
): Observable<T[]> {
235246
const observable = this.getObservable(endpoint, data, method, options);
247+
236248
return observable.pipe(
237249
map((response: Object | Array<Object>) => {
250+
if (options.responseType === 'text') {
251+
return [response as unknown as T];
252+
}
253+
238254
if (Array.isArray(response)) {
239255
return response.map((obj) => {
240256
if (responseClass) {

0 commit comments

Comments
 (0)