mirror of https://github.com/oxen-io/session-ios
Merge branch 'charlesmchen/2fa'
commit
7437152688
@ -1 +1 @@
|
||||
Subproject commit 29babe215072d52688ea5b18592f308bfc190612
|
||||
Subproject commit 267e347b1eb97f8fd2e03a881b1a1c0c1de20e0f
|
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SignalMessaging/OWSViewController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, Enable2FAMode) {
|
||||
OWS2FASettingsMode_Status = 0,
|
||||
OWS2FASettingsMode_SelectPIN,
|
||||
OWS2FASettingsMode_ConfirmPIN,
|
||||
};
|
||||
|
||||
@interface OWS2FASettingsViewController : OWSViewController
|
||||
|
||||
@property (nonatomic) Enable2FAMode mode;
|
||||
|
||||
// When confirming the PIN, this is the PIN that was
|
||||
// initially entered by the user.
|
||||
@property (nonatomic, nullable) NSString *candidatePin;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,492 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWS2FASettingsViewController.h"
|
||||
#import "OWSTableViewController.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "SignalMessaging.h"
|
||||
#import <SignalMessaging/NSString+OWS.h>
|
||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
||||
#import <SignalMessaging/SignalMessaging.h>
|
||||
#import <SignalMessaging/UIColor+OWS.h>
|
||||
#import <SignalMessaging/UIFont+OWS.h>
|
||||
#import <SignalMessaging/UIView+OWS.h>
|
||||
#import <SignalServiceKit/OWS2FAManager.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWS2FASettingsViewController () <UITextFieldDelegate>
|
||||
|
||||
@property (nonatomic, weak) UIViewController *root2FAViewController;
|
||||
|
||||
@property (nonatomic) UITextField *pinTextfield;
|
||||
@property (nonatomic) OWSTableViewController *tableViewController;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWS2FASettingsViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = UIColor.whiteColor;
|
||||
|
||||
self.title = NSLocalizedString(@"ENABLE_2FA_VIEW_TITLE", @"Title for the 'enable two factor auth PIN' views.");
|
||||
|
||||
[self createContents];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(stateDidChange:)
|
||||
name:NSNotificationName_2FAStateDidChange
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)createContents
|
||||
{
|
||||
for (UIView *subview in self.view.subviews) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
|
||||
switch (self.mode) {
|
||||
case OWS2FASettingsMode_Status:
|
||||
[self createStatusContents];
|
||||
break;
|
||||
case OWS2FASettingsMode_SelectPIN:
|
||||
[self createSelectCodeContents];
|
||||
break;
|
||||
case OWS2FASettingsMode_ConfirmPIN:
|
||||
[self createConfirmCodeContents];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
switch (self.mode) {
|
||||
case OWS2FASettingsMode_Status:
|
||||
break;
|
||||
case OWS2FASettingsMode_SelectPIN:
|
||||
case OWS2FASettingsMode_ConfirmPIN:
|
||||
OWSAssert(![OWS2FAManager.sharedManager is2FAEnabled]);
|
||||
break;
|
||||
}
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// If we're using a table, refresh its contents.
|
||||
[self updateTableContents];
|
||||
|
||||
[self updateNavigationItems];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
// If we're using a PIN textfield, select it.
|
||||
[self.pinTextfield becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (UILabel *)createLabelWithText:(NSString *)text
|
||||
{
|
||||
UILabel *label = [UILabel new];
|
||||
label.textColor = [UIColor blackColor];
|
||||
label.text = text;
|
||||
label.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)];
|
||||
label.numberOfLines = 0;
|
||||
label.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
[self.view addSubview:label];
|
||||
return label;
|
||||
}
|
||||
|
||||
- (void)createPinTextfield
|
||||
{
|
||||
self.pinTextfield = [UITextField new];
|
||||
self.pinTextfield.textColor = [UIColor blackColor];
|
||||
switch (self.mode) {
|
||||
case OWS2FASettingsMode_SelectPIN:
|
||||
self.pinTextfield.placeholder = NSLocalizedString(@"ENABLE_2FA_VIEW_SELECT_PIN_DEFAULT_TEXT",
|
||||
@"Text field placeholder for 'two factor auth pin' when selecting a pin.");
|
||||
break;
|
||||
case OWS2FASettingsMode_ConfirmPIN:
|
||||
self.pinTextfield.placeholder = NSLocalizedString(@"ENABLE_2FA_VIEW_CONFIRM_PIN_DEFAULT_TEXT",
|
||||
@"Text field placeholder for 'two factor auth pin' when confirming a pin.");
|
||||
break;
|
||||
case OWS2FASettingsMode_Status:
|
||||
OWSFail(@"%@ invalid mode.", self.logTag) break;
|
||||
}
|
||||
self.pinTextfield.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(30.f, 36.f)];
|
||||
self.pinTextfield.textAlignment = NSTextAlignmentCenter;
|
||||
self.pinTextfield.keyboardType = UIKeyboardTypeNumberPad;
|
||||
self.pinTextfield.delegate = self;
|
||||
self.pinTextfield.secureTextEntry = YES;
|
||||
self.pinTextfield.textAlignment = NSTextAlignmentCenter;
|
||||
[self.pinTextfield addTarget:self
|
||||
action:@selector(textFieldDidChange:)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[self.view addSubview:self.pinTextfield];
|
||||
}
|
||||
|
||||
- (void)createTableView
|
||||
{
|
||||
self.tableViewController = [OWSTableViewController new];
|
||||
[self.view addSubview:self.tableViewController.view];
|
||||
}
|
||||
|
||||
- (void)createStatusContents
|
||||
{
|
||||
const CGFloat kVSpacing = 30.f;
|
||||
|
||||
// TODO: Add hero image?
|
||||
// TODO: Tweak background color?
|
||||
|
||||
NSString *instructions = ([OWS2FAManager.sharedManager is2FAEnabled]
|
||||
? NSLocalizedString(@"ENABLE_2FA_VIEW_STATUS_ENABLED_INSTRUCTIONS",
|
||||
@"Indicates that user has 'two factor auth pin' enabled.")
|
||||
: NSLocalizedString(@"ENABLE_2FA_VIEW_STATUS_DISABLED_INSTRUCTIONS",
|
||||
@"Indicates that user has 'two factor auth pin' disabled."));
|
||||
UILabel *instructionsLabel = [self createLabelWithText:instructions];
|
||||
|
||||
[self createTableView];
|
||||
|
||||
[instructionsLabel autoPinToTopLayoutGuideOfViewController:self withInset:kVSpacing];
|
||||
[instructionsLabel autoPinWidthToSuperviewWithMargin:self.hMargin];
|
||||
|
||||
[self.tableViewController.view autoPinWidthToSuperview];
|
||||
[self.tableViewController.view autoPinEdge:ALEdgeTop
|
||||
toEdge:ALEdgeBottom
|
||||
ofView:instructionsLabel
|
||||
withOffset:kVSpacing];
|
||||
[self.tableViewController.view autoPinToBottomLayoutGuideOfViewController:self withInset:0];
|
||||
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (void)createSelectCodeContents
|
||||
{
|
||||
[self createEnterPINContentsWithInstructions:NSLocalizedString(@"ENABLE_2FA_VIEW_SELECT_PIN_INSTRUCTIONS",
|
||||
@"Indicates that user should select a 'two factor auth pin'.")];
|
||||
}
|
||||
|
||||
- (void)createConfirmCodeContents
|
||||
{
|
||||
[self
|
||||
createEnterPINContentsWithInstructions:NSLocalizedString(@"ENABLE_2FA_VIEW_CONFIRM_PIN_INSTRUCTIONS",
|
||||
@"Indicates that user should confirm their 'two factor auth pin'.")];
|
||||
}
|
||||
|
||||
- (CGFloat)hMargin
|
||||
{
|
||||
return 20.f;
|
||||
}
|
||||
|
||||
- (void)createEnterPINContentsWithInstructions:(NSString *)instructionsText
|
||||
{
|
||||
const CGFloat kVSpacing = 30.f;
|
||||
|
||||
UILabel *instructionsLabel = [self createLabelWithText:instructionsText];
|
||||
|
||||
[self createPinTextfield];
|
||||
|
||||
[instructionsLabel autoPinTopToSuperviewWithMargin:kVSpacing];
|
||||
[instructionsLabel autoPinWidthToSuperviewWithMargin:self.hMargin];
|
||||
|
||||
[self.pinTextfield autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:instructionsLabel withOffset:kVSpacing];
|
||||
[self.pinTextfield autoPinWidthToSuperviewWithMargin:self.hMargin];
|
||||
[self.pinTextfield autoHCenterInSuperview];
|
||||
|
||||
UIView *underscoreView = [UIView new];
|
||||
underscoreView.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1.f];
|
||||
[self.view addSubview:underscoreView];
|
||||
[underscoreView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.pinTextfield withOffset:3];
|
||||
[underscoreView autoPinWidthToSuperviewWithMargin:self.hMargin];
|
||||
[underscoreView autoHCenterInSuperview];
|
||||
[underscoreView autoSetDimension:ALDimensionHeight toSize:1.f];
|
||||
|
||||
[self updateNavigationItems];
|
||||
}
|
||||
|
||||
- (void)updateTableContents
|
||||
{
|
||||
__weak OWS2FASettingsViewController *weakSelf = self;
|
||||
|
||||
// Only some modes use a table.
|
||||
switch (self.mode) {
|
||||
case OWS2FASettingsMode_Status: {
|
||||
OWSTableContents *contents = [OWSTableContents new];
|
||||
OWSTableSection *section = [OWSTableSection new];
|
||||
if ([OWS2FAManager.sharedManager is2FAEnabled]) {
|
||||
[section
|
||||
addItem:[OWSTableItem disclosureItemWithText:
|
||||
NSLocalizedString(@"ENABLE_2FA_VIEW_DISABLE_2FA",
|
||||
@"Label for the 'enable two-factor auth' item in the settings view")
|
||||
actionBlock:^{
|
||||
[weakSelf tryToDisable2FA];
|
||||
}]];
|
||||
} else {
|
||||
[section
|
||||
addItem:[OWSTableItem disclosureItemWithText:
|
||||
NSLocalizedString(@"ENABLE_2FA_VIEW_ENABLE_2FA",
|
||||
@"Label for the 'enable two-factor auth' item in the settings view")
|
||||
actionBlock:^{
|
||||
[weakSelf showEnable2FAWorkUI];
|
||||
}]];
|
||||
}
|
||||
[contents addSection:section];
|
||||
self.tableViewController.contents = contents;
|
||||
break;
|
||||
}
|
||||
case OWS2FASettingsMode_SelectPIN:
|
||||
case OWS2FASettingsMode_ConfirmPIN:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldHaveNextButton
|
||||
{
|
||||
switch (self.mode) {
|
||||
case OWS2FASettingsMode_Status:
|
||||
return NO;
|
||||
case OWS2FASettingsMode_SelectPIN:
|
||||
case OWS2FASettingsMode_ConfirmPIN:
|
||||
return [self hasValidPin];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateNavigationItems
|
||||
{
|
||||
// Note: This affects how the "back" button will look if another
|
||||
// view is pushed on top of this one, not how the "back"
|
||||
// button looks when this view is visible.
|
||||
self.navigationItem.backBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON",
|
||||
@"button text for back button on verification view")
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(backButtonWasPressed)];
|
||||
|
||||
if (self.shouldHaveNextButton) {
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
|
||||
initWithTitle:NSLocalizedString(@"ENABLE_2FA_VIEW_NEXT_BUTTON",
|
||||
@"Label for the 'next' button in the 'enable two factor auth' views.")
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(nextButtonWasPressed)];
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textField:(UITextField *)textField
|
||||
shouldChangeCharactersInRange:(NSRange)range
|
||||
replacementString:(NSString *)insertionText
|
||||
{
|
||||
// TODO: ?
|
||||
const NSUInteger kMaxPinLength = 14;
|
||||
|
||||
// * We only want to let the user enter decimal digits.
|
||||
// * The user should be able to copy and paste freely.
|
||||
// * Invalid input should be simply ignored.
|
||||
//
|
||||
// We accomplish this by being permissive and trying to "take as much of the user
|
||||
// input as possible".
|
||||
//
|
||||
// * Always accept deletes.
|
||||
// * Ignore invalid input.
|
||||
// * Take partial input if possible.
|
||||
|
||||
NSString *oldText = textField.text;
|
||||
// Construct the new contents of the text field by:
|
||||
// 1. Determining the "left" substring: the contents of the old text _before_ the deletion range.
|
||||
// Filtering will remove non-decimal digit characters.
|
||||
NSString *left = [oldText substringToIndex:range.location].digitsOnly;
|
||||
// 2. Determining the "right" substring: the contents of the old text _after_ the deletion range.
|
||||
NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly;
|
||||
// 3. Determining the "center" substring: the contents of the new insertion text.
|
||||
NSString *center = insertionText.digitsOnly;
|
||||
// 4. Construct the "raw" new text by concatenating left, center and right.
|
||||
NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right];
|
||||
// 5. Ensure we don't exceed the maximum length for a PIN.
|
||||
if (textAfterChange.length > kMaxPinLength) {
|
||||
textAfterChange = [textAfterChange substringToIndex:kMaxPinLength];
|
||||
}
|
||||
// 6. Construct the final text.
|
||||
textField.text = textAfterChange;
|
||||
NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length);
|
||||
UITextPosition *pos =
|
||||
[textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterChange];
|
||||
[textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]];
|
||||
|
||||
[self updateNavigationItems];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange:(id)sender
|
||||
{
|
||||
[self updateNavigationItems];
|
||||
}
|
||||
|
||||
#pragma mark - Events
|
||||
|
||||
- (void)nextButtonWasPressed
|
||||
{
|
||||
switch (self.mode) {
|
||||
case OWS2FASettingsMode_Status:
|
||||
OWSFail(@"%@ status mode should not have a next button.", self.logTag);
|
||||
return;
|
||||
case OWS2FASettingsMode_SelectPIN: {
|
||||
OWSAssert(self.hasValidPin);
|
||||
|
||||
OWS2FASettingsViewController *vc = [OWS2FASettingsViewController new];
|
||||
vc.mode = OWS2FASettingsMode_ConfirmPIN;
|
||||
vc.candidatePin = self.pinTextfield.text;
|
||||
OWSAssert(self.root2FAViewController);
|
||||
vc.root2FAViewController = self.root2FAViewController;
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
break;
|
||||
}
|
||||
case OWS2FASettingsMode_ConfirmPIN: {
|
||||
OWSAssert(self.hasValidPin);
|
||||
|
||||
if ([self.pinTextfield.text isEqualToString:self.candidatePin]) {
|
||||
[self tryToEnable2FA];
|
||||
} else {
|
||||
// Clear the PIN so that the user can try again.
|
||||
self.pinTextfield.text = nil;
|
||||
|
||||
[OWSAlerts
|
||||
showAlertWithTitle:NSLocalizedString(@"ALERT_ERROR_TITLE", @"")
|
||||
message:NSLocalizedString(@"ENABLE_2FA_VIEW_PIN_DOES_NOT_MATCH",
|
||||
@"Error indicating that the entered 'two-factor auth PINs' do not match.")];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasValidPin
|
||||
{
|
||||
const NSUInteger kMinPinLength = 4;
|
||||
return self.pinTextfield.text.length >= kMinPinLength;
|
||||
}
|
||||
|
||||
- (void)showEnable2FAWorkUI
|
||||
{
|
||||
OWSAssert(![OWS2FAManager.sharedManager is2FAEnabled]);
|
||||
|
||||
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
OWS2FASettingsViewController *vc = [OWS2FASettingsViewController new];
|
||||
vc.mode = OWS2FASettingsMode_SelectPIN;
|
||||
vc.root2FAViewController = self;
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
}
|
||||
|
||||
- (void)tryToDisable2FA
|
||||
{
|
||||
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
__weak OWS2FASettingsViewController *weakSelf = self;
|
||||
|
||||
[ModalActivityIndicatorViewController
|
||||
presentFromViewController:self
|
||||
canCancel:NO
|
||||
backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
|
||||
[OWS2FAManager.sharedManager disable2FAWithSuccess:^{
|
||||
[modalActivityIndicator dismissWithCompletion:^{
|
||||
// TODO: Should we show an alert?
|
||||
|
||||
[weakSelf updateTableContents];
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
[modalActivityIndicator dismissWithCompletion:^{
|
||||
[weakSelf updateTableContents];
|
||||
|
||||
[OWSAlerts
|
||||
showAlertWithTitle:NSLocalizedString(@"ALERT_ERROR_TITLE", @"")
|
||||
message:NSLocalizedString(@"ENABLE_2FA_VIEW_COULD_NOT_DISABLE_2FA",
|
||||
@"Error indicating that attempt to disable 'two-factor "
|
||||
@"auth' failed.")];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)tryToEnable2FA
|
||||
{
|
||||
OWSAssert(self.candidatePin.length > 0);
|
||||
|
||||
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
__weak OWS2FASettingsViewController *weakSelf = self;
|
||||
|
||||
[ModalActivityIndicatorViewController
|
||||
presentFromViewController:self
|
||||
canCancel:NO
|
||||
backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
|
||||
[OWS2FAManager.sharedManager enable2FAWithPin:self.candidatePin
|
||||
success:^{
|
||||
[modalActivityIndicator dismissWithCompletion:^{
|
||||
[weakSelf showCompleteUI];
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
[modalActivityIndicator dismissWithCompletion:^{
|
||||
// The client may have fallen out of sync with the service.
|
||||
// Try to get back to a known good state by disabling 2FA
|
||||
// whenever enabling it fails.
|
||||
[OWS2FAManager.sharedManager disable2FAWithSuccess:nil failure:nil];
|
||||
|
||||
[weakSelf updateTableContents];
|
||||
|
||||
[OWSAlerts
|
||||
showAlertWithTitle:NSLocalizedString(@"ALERT_ERROR_TITLE", @"")
|
||||
message:NSLocalizedString(@"ENABLE_2FA_VIEW_COULD_NOT_ENABLE_2FA",
|
||||
@"Error indicating that attempt to enable 'two-factor "
|
||||
@"auth' failed.")];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showCompleteUI
|
||||
{
|
||||
OWSAssert([OWS2FAManager.sharedManager is2FAEnabled]);
|
||||
OWSAssert(self.root2FAViewController);
|
||||
|
||||
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
[self.navigationController popToViewController:self.root2FAViewController animated:NO];
|
||||
}
|
||||
|
||||
- (void)backButtonWasPressed
|
||||
{
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)stateDidChange:(NSNotification *)notification
|
||||
{
|
||||
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
if (self.mode == OWS2FASettingsMode_Status) {
|
||||
[self createContents];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,9 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSTableViewController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PrivacySettingsTableViewController : OWSTableViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSRequest;
|
||||
|
||||
@interface OWSRequestFactory : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (TSRequest *)enable2FARequestWithPin:(NSString *)pin;
|
||||
|
||||
+ (TSRequest *)disable2FARequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSRequestFactory.h"
|
||||
#import "TSConstants.h"
|
||||
#import "TSRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSRequestFactory
|
||||
|
||||
+ (TSRequest *)enable2FARequestWithPin:(NSString *)pin;
|
||||
{
|
||||
OWSAssert(pin.length > 0);
|
||||
|
||||
return [TSRequest requestWithUrl:[NSURL URLWithString:textSecure2FAAPI]
|
||||
method:@"PUT"
|
||||
parameters:@{
|
||||
@"pin" : pin,
|
||||
}];
|
||||
}
|
||||
|
||||
+ (TSRequest *)disable2FARequest
|
||||
{
|
||||
return [TSRequest requestWithUrl:[NSURL URLWithString:textSecure2FAAPI] method:@"DELETE" parameters:@{}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,15 +1,15 @@
|
||||
//
|
||||
// TSRegisterForPushRequest.h
|
||||
// TextSecureiOS
|
||||
//
|
||||
// Created by Frederic Jacobs on 10/13/13.
|
||||
// Copyright (c) 2013 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TSRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TSRegisterForPushRequest : TSRequest
|
||||
|
||||
- (id)initWithPushIdentifier:(NSString *)identifier voipIdentifier:(NSString *)voipId;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -1,11 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@interface TSRequest : NSMutableURLRequest
|
||||
|
||||
@property (nonatomic, retain) NSMutableDictionary *parameters;
|
||||
@property (nonatomic) NSDictionary *parameters;
|
||||
|
||||
- (void)makeAuthenticatedRequest;
|
||||
+ (instancetype)requestWithUrl:(NSURL *)url
|
||||
method:(NSString *)method
|
||||
parameters:(NSDictionary<NSString *, id> *)parameters;
|
||||
|
||||
@end
|
||||
|
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const NSNotificationName_2FAStateDidChange;
|
||||
|
||||
typedef void (^OWS2FASuccess)(void);
|
||||
typedef void (^OWS2FAFailure)(NSError *error);
|
||||
|
||||
// This class can be safely accessed and used from any thread.
|
||||
@interface OWS2FAManager : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (instancetype)sharedManager;
|
||||
|
||||
- (BOOL)is2FAEnabled;
|
||||
|
||||
- (void)enable2FAWithPin:(NSString *)pin
|
||||
success:(nullable OWS2FASuccess)success
|
||||
failure:(nullable OWS2FAFailure)failure;
|
||||
|
||||
- (void)disable2FAWithSuccess:(nullable OWS2FASuccess)success failure:(nullable OWS2FAFailure)failure;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWS2FAManager.h"
|
||||
#import "NSNotificationCenter+OWS.h"
|
||||
#import "OWSRequestFactory.h"
|
||||
#import "TSNetworkManager.h"
|
||||
#import "TSStorageManager.h"
|
||||
#import "YapDatabaseConnection+OWS.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const NSNotificationName_2FAStateDidChange = @"NSNotificationName_2FAStateDidChange";
|
||||
|
||||
NSString *const kOWS2FAManager_Collection = @"kOWS2FAManager_Collection";
|
||||
NSString *const kOWS2FAManager_IsEnabledKey = @"kOWS2FAManager_IsEnabledKey";
|
||||
|
||||
@interface OWS2FAManager ()
|
||||
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
@property (nonatomic, readonly) TSNetworkManager *networkManager;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWS2FAManager
|
||||
|
||||
+ (instancetype)sharedManager
|
||||
{
|
||||
static OWS2FAManager *sharedMyManager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedMyManager = [[self alloc] initDefault];
|
||||
});
|
||||
return sharedMyManager;
|
||||
}
|
||||
|
||||
- (instancetype)initDefault
|
||||
{
|
||||
TSStorageManager *storageManager = [TSStorageManager sharedManager];
|
||||
TSNetworkManager *networkManager = [TSNetworkManager sharedManager];
|
||||
|
||||
return [self initWithStorageManager:storageManager networkManager:networkManager];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
|
||||
networkManager:(TSNetworkManager *)networkManager
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
OWSAssert(storageManager);
|
||||
OWSAssert(networkManager);
|
||||
|
||||
_dbConnection = storageManager.newDatabaseConnection;
|
||||
_networkManager = networkManager;
|
||||
|
||||
OWSSingletonAssert();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)is2FAEnabled
|
||||
{
|
||||
return [self.dbConnection boolForKey:kOWS2FAManager_IsEnabledKey
|
||||
inCollection:kOWS2FAManager_Collection
|
||||
defaultValue:NO];
|
||||
}
|
||||
|
||||
- (void)setIs2FAEnabled:(BOOL)value
|
||||
{
|
||||
[self.dbConnection setBool:value forKey:kOWS2FAManager_IsEnabledKey inCollection:kOWS2FAManager_Collection];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:NSNotificationName_2FAStateDidChange
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)enable2FAWithPin:(NSString *)pin success:(nullable OWS2FASuccess)success failure:(nullable OWS2FAFailure)failure
|
||||
{
|
||||
OWSAssert(pin.length > 0);
|
||||
OWSAssert(success);
|
||||
OWSAssert(failure);
|
||||
|
||||
TSRequest *request = [OWSRequestFactory enable2FARequestWithPin:pin];
|
||||
[self.networkManager makeRequest:request
|
||||
success:^(NSURLSessionDataTask *task, id responseObject) {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self setIs2FAEnabled:YES];
|
||||
|
||||
if (success) {
|
||||
success();
|
||||
}
|
||||
}
|
||||
failure:^(NSURLSessionDataTask *task, NSError *error) {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (failure) {
|
||||
failure(error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)disable2FAWithSuccess:(nullable OWS2FASuccess)success failure:(nullable OWS2FAFailure)failure
|
||||
{
|
||||
TSRequest *request = [OWSRequestFactory disable2FARequest];
|
||||
[self.networkManager makeRequest:request
|
||||
success:^(NSURLSessionDataTask *task, id responseObject) {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self setIs2FAEnabled:NO];
|
||||
|
||||
if (success) {
|
||||
success();
|
||||
}
|
||||
}
|
||||
failure:^(NSURLSessionDataTask *task, NSError *error) {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (failure) {
|
||||
failure(error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Loading…
Reference in New Issue