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

View File

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

View File

@ -1,10 +1,15 @@
import 'dart:convert';
import 'dart:io';
import 'package:encrypt/encrypt.dart';
import 'package:manager_app/Models/session.dart';
import 'package:path_provider/path_provider.dart';
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 {
final directory = await getApplicationDocumentsDirectory();
@ -19,21 +24,40 @@ class SessionHelper {
Future<File> writeSession(Session session) async {
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
return file.writeAsString(session.toString());
return file.writeAsString(jsonEncode(session.toMap()));
}
Future<int> readSession() async {
Future<Session> readSession() async {
try {
final file = await _localFile;
// Read the file
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) {
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
return 0;
return Session(rememberMe: false);
}
}
}

View File

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

View File

@ -1,14 +1,14 @@
class Session {
bool isRememberMe;
bool rememberMe;
String host;
String email;
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) {
return new Session(
isRememberMe: json['isRememberMe'] as bool,
rememberMe: json['rememberMe'] as bool,
host: json['host'] as String,
email: json['email'] as String,
password: json['password'] as String
@ -17,7 +17,7 @@ class Session {
Map<String, dynamic> toMap() {
return {
'isRememberMe': isRememberMe,
'rememberMe': rememberMe,
'host': host,
'email': email,
'password': password
@ -26,6 +26,6 @@ class Session {
@override
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/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/main.dart';
import 'package:provider/provider.dart';
@ -129,7 +130,9 @@ class _BodyState extends State<Body> {
),
IconButton(
icon: Icon(Icons.logout),
onPressed: () {
onPressed: () async {
var session = await loadJsonSessionFile();
setState(() {
print("Logout");
ManagerAppContext managerAppContext = appContext.getContext();
@ -140,7 +143,7 @@ class _BodyState extends State<Body> {
context,
MaterialPageRoute(
builder: (context) {
return LoginScreen();
return LoginScreen(session: session);
},
),
(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/services.dart';
import 'package:manager_app/Components/loading.dart';
import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/rounded_input_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/session.dart';
import 'package:manager_app/Screens/Main/main_screen.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/client.dart';
import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key: key);
final Session session;
LoginScreen({Key key, this.session}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
@ -39,7 +37,6 @@ class _LoginScreenState extends State<LoginScreen> {
clientAPI = Client(this.host);
var isError = true;
if (this.email != null && this.password != null && this.host != null) {
// if () {} // Add if token exist and not null + not expired
@ -55,12 +52,14 @@ class _LoginScreenState extends State<LoginScreen> {
setAccessToken(token.accessToken);
if (isRememberMe) {
Session updatedSession = new Session(rememberMe: isRememberMe, host: host, email: email, password: password);
print("REMBER ME TEST SESSIOn");
// update JSON FILE
SessionHelper().writeSession(updatedSession);
}
showNotification(Colors.lightGreen, kWhite, 'Connexion réussie', context);
isError = false;
// Set the appContext
if (appContext.getContext() == null) {
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) {
@ -143,140 +108,101 @@ class _LoginScreenState extends State<LoginScreen> {
@override
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();
}
Future<String>_loadFromAsset() async { // TODO remove and use JsonHelper
return await rootBundle.loadString("assets/files/session.json");
}
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size;
return Scaffold(
body: FutureBuilder( // TODO REMOVE IT and replace by JSON Helper (load file in main) (to setState ok rememberMe)
future: loadJsonSessionFile(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Center(
child: SingleChildScrollView(
child: Container(
height: size.height *0.5,
width: size.width *0.5,
decoration: BoxDecoration(
color: kWhite,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [
BoxShadow(
color: kWhite.withOpacity(0.3),
spreadRadius: 0.5,
blurRadius: 0.5,
offset: Offset(0, 1.5), // changes position of shadow
body: Center(
child: SingleChildScrollView(
child: Container(
height: size.height *0.65,
width: size.width *0.5,
decoration: BoxDecoration(
color: kWhite,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [
BoxShadow(
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()
),
],
),
),
),
),
);
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
height: size.height * 0.2,
SizedBox(height: size.height * 0.05),
!isLoading ? RoundedButton(
text: "LOGIN",
fontSize: 25,
press: () {
authenticateTRY(appContext);
},
): Container(
height: size.height * 0.1,
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:flutter/material.dart';
import 'package:provider/provider.dart';
import 'Helpers/SessionHelper.dart';
import 'Models/session.dart';
import 'Screens/login_screen.dart';
import 'app_context.dart';
import 'constants.dart';
@ -21,8 +23,11 @@ Future<void> main() async {
initialRoute = '/welcome';
var session = await loadJsonSessionFile();
final MyApp myApp = MyApp(
initialRoute: initialRoute,
session: session
//context: localContext,
);
@ -31,8 +36,9 @@ Future<void> main() async {
class MyApp extends StatefulWidget {
final String initialRoute;
final Session session;
final ManagerAppContext managerAppContext;
MyApp({this.initialRoute, this.managerAppContext});
MyApp({this.initialRoute, this.session, this.managerAppContext});
// This widget is the root of your application.
@override
@ -60,7 +66,7 @@ class _MyAppState extends State<MyApp> {
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: {
'/welcome': (context) => LoginScreen(),
'/welcome': (context) => LoginScreen(session: widget.session),
'/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
# See https://dart.dev/tools/pub/glossary#lockfile
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:
dependency: transitive
description:
@ -64,13 +78,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
crypt:
dependency: "direct main"
description:
name: crypt
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
crypto:
dependency: transitive
description:
@ -106,6 +113,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
encrypt:
dependency: "direct main"
description:
name: encrypt
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
fake_async:
dependency: transitive
description:
@ -296,6 +310,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
process:
dependency: transitive
description:

View File

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