Remember me + encrypt password in json file !

This commit is contained in:
Thomas Fransolet 2021-07-29 16:14:39 +02:00
parent d9fc06b126
commit 86e4beb362
10 changed files with 179 additions and 186 deletions

View File

@ -9,6 +9,7 @@ class RoundedInputField extends StatelessWidget {
final String initialValue; final String initialValue;
final Color color, textColor, iconColor; final Color color, textColor, iconColor;
final int maxLength; final int maxLength;
final bool isEmail;
const RoundedInputField({ const RoundedInputField({
Key key, Key key,
this.hintText, this.hintText,
@ -19,6 +20,7 @@ class RoundedInputField extends StatelessWidget {
this.iconColor = kPrimaryColor, this.iconColor = kPrimaryColor,
this.onChanged, this.onChanged,
this.maxLength, // 50 this.maxLength, // 50
this.isEmail = false
}) : super(key: key); }) : super(key: key);
@override @override
@ -30,6 +32,7 @@ class RoundedInputField extends StatelessWidget {
initialValue: initialValue, initialValue: initialValue,
cursorColor: textColor, cursorColor: textColor,
maxLength: maxLength, maxLength: maxLength,
keyboardType: isEmail ? TextInputType.emailAddress : TextInputType.text,
style: TextStyle(fontSize: 20, color: textColor), style: TextStyle(fontSize: 20, color: textColor),
decoration: InputDecoration( decoration: InputDecoration(
icon: icon != null ? Icon( icon: icon != null ? Icon(

View File

@ -4,9 +4,11 @@ import 'package:manager_app/constants.dart';
class RoundedPasswordField extends StatefulWidget { class RoundedPasswordField extends StatefulWidget {
final ValueChanged<String> onChanged; final ValueChanged<String> onChanged;
final String initialValue;
const RoundedPasswordField({ const RoundedPasswordField({
Key key, Key key,
this.onChanged, this.onChanged,
this.initialValue,
}) : super(key: key); }) : super(key: key);
@override @override
@ -19,9 +21,10 @@ class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return TextFieldContainer( return TextFieldContainer(
child: TextField( child: TextFormField(
obscureText: isVisible, obscureText: isVisible,
onChanged: widget.onChanged, onChanged: widget.onChanged,
initialValue: widget.initialValue,
cursorColor: kPrimaryColor, cursorColor: kPrimaryColor,
style: TextStyle(fontSize: 20, color: kBlack), style: TextStyle(fontSize: 20, color: kBlack),
decoration: InputDecoration( decoration: InputDecoration(

View File

@ -1,10 +1,15 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:encrypt/encrypt.dart';
import 'package:manager_app/Models/session.dart'; import 'package:manager_app/Models/session.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
class SessionHelper { class SessionHelper {
// TODO instance LOAD FILE.. final key = Key.fromUtf8('aVs:ZMe3EK-yS<y:;k>vCGrj3T8]yG6E');
final iv = IV.fromLength(16);
Future<String> get _localPath async { Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory(); final directory = await getApplicationDocumentsDirectory();
@ -19,21 +24,40 @@ class SessionHelper {
Future<File> writeSession(Session session) async { Future<File> writeSession(Session session) async {
final file = await _localFile; final file = await _localFile;
if (session.password != null) {
var encrypter = Encrypter(AES(key));
var encrypted = encrypter.encrypt(session.password, iv: iv);
session.password = encrypted.base64;
}
// Write the file // Write the file
return file.writeAsString(session.toString()); return file.writeAsString(jsonEncode(session.toMap()));
} }
Future<int> readSession() async { Future<Session> readSession() async {
try { try {
final file = await _localFile; final file = await _localFile;
// Read the file // Read the file
final contents = await file.readAsString(); final contents = await file.readAsString();
return int.parse(contents); Session session = Session.fromJson(jsonDecode(contents));
var encrypter = Encrypter(AES(key));
if (session.password != null) {
session.password = encrypter.decrypt64(session.password, iv: iv);
}
return session;
} catch (e) { } catch (e) {
if (e.toString().contains("cannot find the file")) {
final path = await _localPath;
new File('$path/session.json').createSync(recursive: true);
print("file created");
await writeSession(new Session(rememberMe: false));
}
// If encountering an error, return 0 // If encountering an error, return 0
return 0; return Session(rememberMe: false);
} }
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:manager_app/Models/session.dart';
import 'package:manager_app/client.dart'; import 'package:manager_app/client.dart';
import 'package:managerapi/api.dart'; import 'package:managerapi/api.dart';

View File

@ -1,14 +1,14 @@
class Session { class Session {
bool isRememberMe; bool rememberMe;
String host; String host;
String email; String email;
String password; String password;
Session({this.isRememberMe, this.host, this.email, this.password}); Session({this.rememberMe, this.host, this.email, this.password});
factory Session.fromJson(Map<String, dynamic> json) { factory Session.fromJson(Map<String, dynamic> json) {
return new Session( return new Session(
isRememberMe: json['isRememberMe'] as bool, rememberMe: json['rememberMe'] as bool,
host: json['host'] as String, host: json['host'] as String,
email: json['email'] as String, email: json['email'] as String,
password: json['password'] as String password: json['password'] as String
@ -17,7 +17,7 @@ class Session {
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
'isRememberMe': isRememberMe, 'rememberMe': rememberMe,
'host': host, 'host': host,
'email': email, 'email': email,
'password': password 'password': password
@ -26,6 +26,6 @@ class Session {
@override @override
String toString() { String toString() {
return '{isRememberMe: $isRememberMe, host: $host, email: $email, password: $password}'; return '{rememberMe: $rememberMe, host: $host, email: $email, password: $password}';
} }
} }

View File

@ -12,6 +12,7 @@ import 'package:manager_app/Screens/Resources/resources_screen.dart';
import 'package:manager_app/Screens/login_screen.dart'; import 'package:manager_app/Screens/login_screen.dart';
import 'package:manager_app/app_context.dart'; import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
import 'package:manager_app/main.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -129,7 +130,9 @@ class _BodyState extends State<Body> {
), ),
IconButton( IconButton(
icon: Icon(Icons.logout), icon: Icon(Icons.logout),
onPressed: () { onPressed: () async {
var session = await loadJsonSessionFile();
setState(() { setState(() {
print("Logout"); print("Logout");
ManagerAppContext managerAppContext = appContext.getContext(); ManagerAppContext managerAppContext = appContext.getContext();
@ -140,7 +143,7 @@ class _BodyState extends State<Body> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) { builder: (context) {
return LoginScreen(); return LoginScreen(session: session);
}, },
), ),
(Route<dynamic> route) => false // For pushAndRemoveUntil (Route<dynamic> route) => false // For pushAndRemoveUntil

View File

@ -1,24 +1,22 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:manager_app/Components/loading.dart'; import 'package:manager_app/Components/loading.dart';
import 'package:manager_app/Components/message_notification.dart'; import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Components/rounded_button.dart'; import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/rounded_input_field.dart'; import 'package:manager_app/Components/rounded_input_field.dart';
import 'package:manager_app/Components/rounded_password_field.dart'; import 'package:manager_app/Components/rounded_password_field.dart';
import 'package:manager_app/Helpers/SessionHelper.dart';
import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/Models/session.dart';
import 'package:manager_app/Screens/Main/main_screen.dart'; import 'package:manager_app/Screens/Main/main_screen.dart';
import 'package:manager_app/app_context.dart'; import 'package:manager_app/app_context.dart';
import 'package:manager_app/client.dart'; import 'package:manager_app/client.dart';
import 'package:manager_app/constants.dart'; import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart'; import 'package:managerapi/api.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget { class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key: key); final Session session;
LoginScreen({Key key, this.session}) : super(key: key);
@override @override
_LoginScreenState createState() => _LoginScreenState(); _LoginScreenState createState() => _LoginScreenState();
@ -39,7 +37,6 @@ class _LoginScreenState extends State<LoginScreen> {
clientAPI = Client(this.host); clientAPI = Client(this.host);
var isError = true;
if (this.email != null && this.password != null && this.host != null) { if (this.email != null && this.password != null && this.host != null) {
// if () {} // Add if token exist and not null + not expired // if () {} // Add if token exist and not null + not expired
@ -55,12 +52,14 @@ class _LoginScreenState extends State<LoginScreen> {
setAccessToken(token.accessToken); setAccessToken(token.accessToken);
if (isRememberMe) { if (isRememberMe) {
Session updatedSession = new Session(rememberMe: isRememberMe, host: host, email: email, password: password);
print("REMBER ME TEST SESSIOn");
// update JSON FILE // update JSON FILE
SessionHelper().writeSession(updatedSession);
} }
showNotification(Colors.lightGreen, kWhite, 'Connexion réussie', context); showNotification(Colors.lightGreen, kWhite, 'Connexion réussie', context);
isError = false;
// Set the appContext // Set the appContext
if (appContext.getContext() == null) { if (appContext.getContext() == null) {
ManagerAppContext managerAppContext = new ManagerAppContext(); ManagerAppContext managerAppContext = new ManagerAppContext();
@ -97,40 +96,6 @@ class _LoginScreenState extends State<LoginScreen> {
}); });
} }
} }
if (!isError) {
/*UserDetailDTO user = await clientAPI.userApi.userGetDetail("6071f74aaf542f03116f2b9b");
print("user values ??");
print(user.email);
print(user.id);
List<ConfigurationDTO> configurations = await clientAPI.configurationApi.configurationGet();
print("number of configurations " + configurations.length.toString());
configurations.forEach((element) {
print(element);
});
/*List<SectionDTO> sections = await clientAPI.sectionApi.sectionGetFromConfiguration(id);
print("number of sections " + sections.length.toString());
sections.forEach((element) {
print(element);
});*/
SectionDTO section = await clientAPI.sectionApi.sectionGetDetail("60916249494b9eaf283b44f7");
print(section);
List<ResourceDTO> resources = await clientAPI.resourceApi.resourceGet();
print("number of resources " + resources.length.toString());
resources.forEach((element) {
print(element);
});
List<DeviceDTO> devices = await clientAPI.deviceApi.deviceGet();
print("number of devices " + devices.length.toString());
devices.forEach((element) {
print(element);
});*/
}
} }
void setAccessToken(String accessToken) { void setAccessToken(String accessToken) {
@ -143,140 +108,101 @@ class _LoginScreenState extends State<LoginScreen> {
@override @override
void initState() { void initState() {
_loadFromAsset(); this.isRememberMe = widget.session.rememberMe;
this.host = widget.session.host;
this.email = widget.session.email;
this.password = widget.session.password;
super.initState(); super.initState();
} }
Future<String>_loadFromAsset() async { // TODO remove and use JsonHelper
return await rootBundle.loadString("assets/files/session.json");
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context); final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
return Scaffold( return Scaffold(
body: FutureBuilder( // TODO REMOVE IT and replace by JSON Helper (load file in main) (to setState ok rememberMe) body: Center(
future: loadJsonSessionFile(), child: SingleChildScrollView(
builder: (context, AsyncSnapshot<dynamic> snapshot) { child: Container(
if (snapshot.connectionState == ConnectionState.done) { height: size.height *0.65,
return Center( width: size.width *0.5,
child: SingleChildScrollView( decoration: BoxDecoration(
child: Container( color: kWhite,
height: size.height *0.5, borderRadius: BorderRadius.circular(8.0),
width: size.width *0.5, boxShadow: [
decoration: BoxDecoration( BoxShadow(
color: kWhite, color: kWhite.withOpacity(0.3),
borderRadius: BorderRadius.circular(8.0), spreadRadius: 0.5,
boxShadow: [ blurRadius: 0.5,
BoxShadow( offset: Offset(0, 1.5), // changes position of shadow
color: kWhite.withOpacity(0.3), ),
spreadRadius: 0.5, ],
blurRadius: 0.5, ),
offset: Offset(0, 1.5), // changes position of shadow child: Padding(
padding: const EdgeInsets.only(left: 60.0, right: 60.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RoundedInputField(
hintText: "Host",
onChanged: (value) {
host = value;
},
initialValue: host,
icon: Icons.home
),
RoundedInputField(
hintText: "Email",
onChanged: (value) {
email = value;
},
icon: Icons.person,
initialValue: email,
isEmail: true,
),
RoundedPasswordField(
initialValue: password,
onChanged: (value) {
password = value;
},
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
checkColor: kTextLightColor,
activeColor: kPrimaryColor,
value: this.isRememberMe,
onChanged: (bool value) {
setState(() {
this.isRememberMe = value;
});
},
), ),
Text("Se souvenir de moi", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),),
], ],
), ),
child: Padding(
padding: const EdgeInsets.only(left: 60.0, right: 60.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RoundedInputField(
hintText: "Host",
onChanged: (value) {
host = value;
},
icon: Icons.home
),
RoundedInputField(
hintText: "Email",
onChanged: (value) {
email = value;
},
icon: Icons.person
),
RoundedPasswordField(
onChanged: (value) {
password = value;
},
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
checkColor: kTextLightColor,
activeColor: kPrimaryColor,
value: this.isRememberMe,
onChanged: (bool value) {
setState(() {
this.isRememberMe = value;
});
},
),
Text("Se souvenir de moi", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),),
],
),
),
SizedBox(height: size.height * 0.02),
!isLoading ? RoundedButton(
text: "LOGIN",
fontSize: 25,
press: () {
authenticateTRY(appContext);
},
): Container(
height: size.height * 0.1,
child: Loading()
),
],
),
),
), ),
), SizedBox(height: size.height * 0.05),
); !isLoading ? RoundedButton(
} else if (snapshot.connectionState == ConnectionState.none) { text: "LOGIN",
return Text("No data"); fontSize: 25,
} else { press: () {
return Center( authenticateTRY(appContext);
child: Container( },
height: size.height * 0.2, ): Container(
height: size.height * 0.1,
child: Loading() child: Loading()
) ),
); ],
} ),
} ),
), ),
),
)
); );
} }
Future loadJsonSessionFile() async { // TODO use jsonHelper in this method
String jsonString = await _loadFromAsset();
final jsonResponse = jsonDecode(jsonString);
try{
var rememberMeJson = jsonResponse.rememberMe != null ? jsonResponse.rememberMe : false;
var hostJson = jsonResponse.host;
var emailJson = jsonResponse.email;
var passwordJson = jsonResponse.password;
if (rememberMeJson && hostJson != null && emailJson != null && passwordJson != null) {
setState(() {
isRememberMe = rememberMeJson;
host = hostJson;
email = emailJson;
password = passwordJson;
});
}
} catch(Exception) {
}
print(jsonResponse);
}
} }

View File

@ -4,6 +4,8 @@ import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/Screens/Main/main_screen.dart'; import 'package:manager_app/Screens/Main/main_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'Helpers/SessionHelper.dart';
import 'Models/session.dart';
import 'Screens/login_screen.dart'; import 'Screens/login_screen.dart';
import 'app_context.dart'; import 'app_context.dart';
import 'constants.dart'; import 'constants.dart';
@ -21,8 +23,11 @@ Future<void> main() async {
initialRoute = '/welcome'; initialRoute = '/welcome';
var session = await loadJsonSessionFile();
final MyApp myApp = MyApp( final MyApp myApp = MyApp(
initialRoute: initialRoute, initialRoute: initialRoute,
session: session
//context: localContext, //context: localContext,
); );
@ -31,8 +36,9 @@ Future<void> main() async {
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
final String initialRoute; final String initialRoute;
final Session session;
final ManagerAppContext managerAppContext; final ManagerAppContext managerAppContext;
MyApp({this.initialRoute, this.managerAppContext}); MyApp({this.initialRoute, this.session, this.managerAppContext});
// This widget is the root of your application. // This widget is the root of your application.
@override @override
@ -60,7 +66,7 @@ class _MyAppState extends State<MyApp> {
visualDensity: VisualDensity.adaptivePlatformDensity, visualDensity: VisualDensity.adaptivePlatformDensity,
), ),
routes: { routes: {
'/welcome': (context) => LoginScreen(), '/welcome': (context) => LoginScreen(session: widget.session),
'/main': (context) => MainScreen() '/main': (context) => MainScreen()
} }
), ),
@ -68,3 +74,9 @@ class _MyAppState extends State<MyApp> {
} }
} }
Future<Session> loadJsonSessionFile() async {
Session session = await SessionHelper().readSession();
print(session);
return session;
}

View File

@ -1,6 +1,20 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -64,13 +78,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
crypt:
dependency: "direct main"
description:
name: crypt
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -106,6 +113,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.2" version: "0.3.2"
encrypt:
dependency: "direct main"
description:
name: encrypt
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -296,6 +310,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
process: process:
dependency: transitive dependency: transitive
description: description:

View File

@ -38,7 +38,7 @@ dependencies:
video_player: ^2.1.1 video_player: ^2.1.1
drag_and_drop_lists: ^0.3.2 drag_and_drop_lists: ^0.3.2
path_provider: ^2.0.2 path_provider: ^2.0.2
crypt: ^4.0.1 encrypt: ^5.0.0
window_size: window_size:
git: git:
url: git://github.com/google/flutter-desktop-embedding.git url: git://github.com/google/flutter-desktop-embedding.git