Add screen to let a returning user enter her existing passphrase

This commit is contained in:
Vishnu Mohandas 2020-08-26 08:04:13 +05:30
parent 9220f2bef8
commit 7a34da5588
5 changed files with 147 additions and 30 deletions

View file

@ -134,6 +134,6 @@ class Configuration {
}
bool hasConfiguredAccount() {
return getEndpoint() != null && getToken() != null;
return getToken() != null && getKey() != null;
}
}

View file

@ -2,10 +2,14 @@ import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/events/user_authenticated_event.dart';
import 'package:photos/file_repository.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/ui/email_entry_page.dart';
import 'package:photos/ui/ott_verification_page.dart';
import 'package:photos/ui/passphrase_entry_page.dart';
import 'package:photos/ui/passphrase_reentry_page.dart';
import 'package:photos/ui/share_folder_widget.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/file_util.dart';
@ -40,14 +44,25 @@ class GalleryAppBarWidget extends StatefulWidget
}
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
StreamSubscription _userAuthEventSubscription;
@override
void initState() {
widget.selectedFiles.addListener(() {
setState(() {});
});
_userAuthEventSubscription =
Bus.instance.on<UserAuthenticatedEvent>().listen((event) {
setState(() {});
});
super.initState();
}
@override
void dispose() {
_userAuthEventSubscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.selectedFiles.files.isEmpty) {
@ -73,11 +88,28 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
List<Widget> actions = List<Widget>();
if (!Configuration.instance.hasConfiguredAccount()) {
actions.add(IconButton(
icon: Icon(Configuration.instance.hasConfiguredAccount()
? Icons.sync_problem
: Icons.sync_disabled),
icon: Icon(Icons.sync_disabled),
onPressed: () {
_navigateToSignInPage(context);
var page;
if (Configuration.instance.getToken() == null) {
page = EmailEntryPage();
} else {
// No key
if (Configuration.instance.getEncryptedKey() != null) {
// Yet to decrypt the key
page = PassphraseReentryPage();
} else {
// Never had a key
page = PassphraseEntryPage();
}
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return page;
},
),
);
},
));
} else if (widget.type == GalleryAppBarType.local_folder &&
@ -169,14 +201,4 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
void _clearSelectedFiles() {
widget.selectedFiles.clearAll();
}
void _navigateToSignInPage(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return EmailEntryPage();
},
),
);
}
}

View file

@ -19,6 +19,7 @@ class _PassphraseEntryPageState extends State<PassphraseEntryPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Icon(Icons.lock),
title: Text("Encryption Passphrase"),
),
body: _getBody(),

View file

@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/svg.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/events/user_authenticated_event.dart';
import 'package:photos/utils/dialog_util.dart';
class PassphraseReentryPage extends StatefulWidget {
PassphraseReentryPage({Key key}) : super(key: key);
@override
_PassphraseReentryPageState createState() => _PassphraseReentryPageState();
}
class _PassphraseReentryPageState extends State<PassphraseReentryPage> {
final _passphraseController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Icon(Icons.lock),
title: Text(
"Encryption Passphrase",
),
),
body: _getBody(),
);
}
Widget _getBody() {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(16, 40, 16, 16),
child: Column(
children: [
SvgPicture.asset(
"assets/vault.svg",
width: 196,
height: 196,
),
Padding(padding: EdgeInsets.all(20)),
Text(
"Please enter your passphrase.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
),
),
Padding(padding: EdgeInsets.all(12)),
TextFormField(
decoration: InputDecoration(
hintText: "that thing you promised to never forget",
contentPadding: EdgeInsets.all(20),
),
controller: _passphraseController,
autofocus: false,
autocorrect: false,
keyboardType: TextInputType.visiblePassword,
onChanged: (_) {
setState(() {});
},
),
Padding(padding: EdgeInsets.all(12)),
SizedBox(
width: double.infinity,
child: RaisedButton(
onPressed: _passphraseController.text.isNotEmpty
? () async {
final dialog =
createProgressDialog(context, "Please wait...");
await dialog.show();
await Configuration.instance
.decryptEncryptedKey(_passphraseController.text);
await dialog.hide();
Bus.instance.fire(UserAuthenticatedEvent());
Navigator.of(context)
.popUntil((route) => route.isFirst);
}
: null,
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
child: Text("Set Passphrase"),
color: Theme.of(context).buttonColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
)),
],
),
),
);
}
}

View file

@ -8,7 +8,9 @@ import 'package:photos/core/event_bus.dart';
import 'package:photos/events/user_authenticated_event.dart';
import 'package:photos/ui/ott_verification_page.dart';
import 'package:photos/ui/passphrase_entry_page.dart';
import 'package:photos/ui/passphrase_reentry_page.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/toast_util.dart';
class UserAuthenticator {
final _dio = Dio();
@ -29,11 +31,9 @@ class UserAuthenticator {
},
).catchError((e) async {
_logger.severe(e);
await dialog.hide();
showGenericErrorDialog(context);
}).then((response) async {
await dialog.hide();
if (response.statusCode == 200) {
if (response != null && response.statusCode == 200) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
@ -58,25 +58,25 @@ class UserAuthenticator {
},
).catchError((e) async {
_logger.severe(e);
await dialog.hide();
showGenericErrorDialog(context);
}).then((response) async {
await dialog.hide();
if (response != null && response.statusCode == 200) {
_saveConfiguration(response);
showToast("Email verification successful!");
var page;
if (Configuration.instance.getEncryptedKey() != null) {
// TODO: Passphrase re-enter to decrypt
Bus.instance.fire(UserAuthenticatedEvent());
Navigator.of(context).popUntil((route) => route.isFirst);
page = PassphraseReentryPage();
} else {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return PassphraseEntryPage();
},
),
);
page = PassphraseEntryPage();
}
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return page;
},
),
(route) => route.isFirst,
);
} else {
showErrorDialog(
context, "Oops.", "Verification failed, please try again.");