Show option to enable/disable email MFA
This commit is contained in:
parent
89774972f8
commit
553a6ce732
4 changed files with 112 additions and 6 deletions
|
@ -1,3 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
@ -10,6 +11,7 @@ class UserDetails {
|
|||
final int sharedCollectionsCount;
|
||||
final Subscription subscription;
|
||||
final FamilyData? familyData;
|
||||
final ProfileData? profileData;
|
||||
|
||||
UserDetails(
|
||||
this.email,
|
||||
|
@ -18,6 +20,7 @@ class UserDetails {
|
|||
this.sharedCollectionsCount,
|
||||
this.subscription,
|
||||
this.familyData,
|
||||
this.profileData,
|
||||
);
|
||||
|
||||
bool isPartOfFamily() {
|
||||
|
@ -59,8 +62,10 @@ class UserDetails {
|
|||
(map['sharedCollectionsCount'] ?? 0) as int,
|
||||
Subscription.fromMap(map['subscription']),
|
||||
FamilyData.fromMap(map['familyData']),
|
||||
ProfileData.fromJson(map['profileData']),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FamilyMember {
|
||||
|
@ -80,7 +85,39 @@ class FamilyMember {
|
|||
);
|
||||
}
|
||||
}
|
||||
class ProfileData {
|
||||
bool canDisableEmailMFA;
|
||||
bool isEmailMFAEnabled;
|
||||
bool isTwoFactorEnabled;
|
||||
|
||||
// Constructor with default values
|
||||
ProfileData({
|
||||
this.canDisableEmailMFA = false,
|
||||
this.isEmailMFAEnabled = false,
|
||||
this.isTwoFactorEnabled = false,
|
||||
});
|
||||
|
||||
// Factory method to create ProfileData instance from JSON
|
||||
factory ProfileData.fromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) null;
|
||||
|
||||
return ProfileData(
|
||||
canDisableEmailMFA: json!['canDisableEmailMFA'] ?? false,
|
||||
isEmailMFAEnabled: json['isEmailMFAEnabled'] ?? false,
|
||||
isTwoFactorEnabled: json['isTwoFactorEnabled'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
// Method to convert ProfileData instance to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'canDisableEmailMFA': canDisableEmailMFA,
|
||||
'isEmailMFAEnabled': isEmailMFAEnabled,
|
||||
'isTwoFactorEnabled': isTwoFactorEnabled,
|
||||
};
|
||||
}
|
||||
String toJsonString() => json.encode(toJson());
|
||||
}
|
||||
class FamilyData {
|
||||
final List<FamilyMember>? members;
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ import "package:uuid/uuid.dart";
|
|||
class UserService {
|
||||
static const keyHasEnabledTwoFactor = "has_enabled_two_factor";
|
||||
static const keyUserDetails = "user_details";
|
||||
static const kCanDisableEmailMFA = "can_disable_email_mfa";
|
||||
static const kIsEmailMFAEnabled = "is_email_mfa_enabled";
|
||||
final SRP6GroupParameters kDefaultSrpGroup = SRP6StandardGroups.rfc5054_4096;
|
||||
final _dio = Network.instance.getDio();
|
||||
final _enteDio = Network.instance.enteDio;
|
||||
|
@ -131,10 +133,9 @@ class UserService {
|
|||
|
||||
|
||||
Future<UserDetails> getUserDetailsV2({
|
||||
bool memoryCount = true,
|
||||
bool shouldCache = false,
|
||||
bool memoryCount = false,
|
||||
bool shouldCache = true,
|
||||
}) async {
|
||||
_logger.info("Fetching user details");
|
||||
try {
|
||||
final response = await _enteDio.get(
|
||||
"/users/details/v2",
|
||||
|
@ -144,14 +145,19 @@ class UserService {
|
|||
);
|
||||
final userDetails = UserDetails.fromMap(response.data);
|
||||
if (shouldCache) {
|
||||
if(userDetails.profileData != null) {
|
||||
_preferences.setBool(kIsEmailMFAEnabled, userDetails.profileData!.isEmailMFAEnabled);
|
||||
_preferences.setBool(kCanDisableEmailMFA, userDetails.profileData!.canDisableEmailMFA);
|
||||
}
|
||||
// handle email change from different client
|
||||
if (userDetails.email != _config.getEmail()) {
|
||||
setEmail(userDetails.email);
|
||||
}
|
||||
}
|
||||
_logger.info("Successfully fetched user details");
|
||||
return userDetails;
|
||||
} on DioError catch (e) {
|
||||
_logger.info(e);
|
||||
} catch(e) {
|
||||
_logger.warning("Failed to fetch", e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
@ -890,7 +896,26 @@ class UserService {
|
|||
}
|
||||
}
|
||||
|
||||
bool canDisableEmailMFA() {
|
||||
return _preferences.getBool(kCanDisableEmailMFA) ?? false;
|
||||
}
|
||||
bool hasEmailMFAEnabled() {
|
||||
return _preferences.getBool(kIsEmailMFAEnabled) ?? true;
|
||||
}
|
||||
|
||||
|
||||
Future<void> updateEmailMFA(bool isEnabled) async {
|
||||
try {
|
||||
await _enteDio.put(
|
||||
"/users/email-mfa",
|
||||
data: {
|
||||
"isEnabled": isEnabled,
|
||||
},
|
||||
);
|
||||
_preferences.setBool(kIsEmailMFAEnabled, isEnabled);
|
||||
} catch (e) {
|
||||
_logger.severe("Failed to update email mfa",e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/account/sessions_page.dart';
|
||||
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
|
||||
|
@ -8,6 +11,7 @@ import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
|
|||
import 'package:ente_auth/ui/components/menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/components/toggle_switch_widget.dart';
|
||||
import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SecuritySectionWidget extends StatefulWidget {
|
||||
|
@ -41,6 +45,8 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
}
|
||||
|
||||
Widget _getSectionOptions(BuildContext context) {
|
||||
final bool canDisableMFA = UserService.instance.canDisableEmailMFA();
|
||||
|
||||
final l10n = context.l10n;
|
||||
final List<Widget> children = [];
|
||||
children.addAll([
|
||||
|
@ -64,6 +70,33 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
},
|
||||
),
|
||||
),
|
||||
if(canDisableMFA)
|
||||
sectionOptionSpacing,
|
||||
if(canDisableMFA)
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Email MFA",
|
||||
),
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => UserService.instance.hasEmailMFAEnabled(),
|
||||
onChanged: () async {
|
||||
final hasAuthenticated = await LocalAuthenticationService
|
||||
.instance
|
||||
.requestLocalAuthentication(
|
||||
context,
|
||||
"Authenticate to change your email MFA setting",
|
||||
);
|
||||
final isEmailMFAEnabled =
|
||||
UserService.instance.hasEmailMFAEnabled();
|
||||
if (hasAuthenticated) {
|
||||
await updateEmailMFA(!isEmailMFAEnabled);
|
||||
if(mounted){
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
|
@ -95,4 +128,12 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateEmailMFA(bool isEnabled) async {
|
||||
try {
|
||||
await UserService.instance.updateEmailMFA(isEnabled);
|
||||
} catch (e) {
|
||||
showToast(context, "Error updating email MFA");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/settings/about_section_widget.dart';
|
||||
|
@ -20,8 +21,10 @@ class SettingsPage extends StatelessWidget {
|
|||
final ValueNotifier<String?> emailNotifier;
|
||||
const SettingsPage({Key? key, required this.emailNotifier}) : super(key: key);
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
UserService.instance.getUserDetailsV2().ignore();
|
||||
final enteColorScheme = getEnteColorScheme(context);
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
|
|
Loading…
Add table
Reference in a new issue