diff --git a/lib/Components/rounded_input_field.dart b/lib/Components/rounded_input_field.dart index af153c0..75e5c07 100644 --- a/lib/Components/rounded_input_field.dart +++ b/lib/Components/rounded_input_field.dart @@ -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( diff --git a/lib/Components/rounded_password_field.dart b/lib/Components/rounded_password_field.dart index e259c1f..055df84 100644 --- a/lib/Components/rounded_password_field.dart +++ b/lib/Components/rounded_password_field.dart @@ -4,9 +4,11 @@ import 'package:manager_app/constants.dart'; class RoundedPasswordField extends StatefulWidget { final ValueChanged onChanged; + final String initialValue; const RoundedPasswordField({ Key key, this.onChanged, + this.initialValue, }) : super(key: key); @override @@ -19,9 +21,10 @@ class _RoundedPasswordFieldState extends State { @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( diff --git a/lib/Helpers/SessionHelper.dart b/lib/Helpers/SessionHelper.dart index 9f776b4..f9951d0 100644 --- a/lib/Helpers/SessionHelper.dart +++ b/lib/Helpers/SessionHelper.dart @@ -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-ySvCGrj3T8]yG6E'); + final iv = IV.fromLength(16); + + Future get _localPath async { final directory = await getApplicationDocumentsDirectory(); @@ -19,21 +24,40 @@ class SessionHelper { Future 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 readSession() async { + Future 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); } } } \ No newline at end of file diff --git a/lib/Models/managerContext.dart b/lib/Models/managerContext.dart index 651d089..28975a0 100644 --- a/lib/Models/managerContext.dart +++ b/lib/Models/managerContext.dart @@ -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'; diff --git a/lib/Models/session.dart b/lib/Models/session.dart index 73679e3..a33910a 100644 --- a/lib/Models/session.dart +++ b/lib/Models/session.dart @@ -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 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 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}'; } } \ No newline at end of file diff --git a/lib/Screens/Main/components/body.dart b/lib/Screens/Main/components/body.dart index 2b31484..7116f9c 100644 --- a/lib/Screens/Main/components/body.dart +++ b/lib/Screens/Main/components/body.dart @@ -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 { ), 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 { context, MaterialPageRoute( builder: (context) { - return LoginScreen(); + return LoginScreen(session: session); }, ), (Route route) => false // For pushAndRemoveUntil diff --git a/lib/Screens/login_screen.dart b/lib/Screens/login_screen.dart index d93d8cb..52c1097 100644 --- a/lib/Screens/login_screen.dart +++ b/lib/Screens/login_screen.dart @@ -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 { 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 { 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 { }); } } - - if (!isError) { - /*UserDetailDTO user = await clientAPI.userApi.userGetDetail("6071f74aaf542f03116f2b9b"); - print("user values ??"); - print(user.email); - print(user.id); - - List configurations = await clientAPI.configurationApi.configurationGet(); - print("number of configurations " + configurations.length.toString()); - configurations.forEach((element) { - print(element); - }); - - /*List 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 resources = await clientAPI.resourceApi.resourceGet(); - print("number of resources " + resources.length.toString()); - resources.forEach((element) { - print(element); - }); - - List 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 { @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_loadFromAsset() async { // TODO remove and use JsonHelper - return await rootBundle.loadString("assets/files/session.json"); - } - @override Widget build(BuildContext context) { final appContext = Provider.of(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 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: [ + 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: [ - 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); - } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 0961131..3fec71b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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 main() async { initialRoute = '/welcome'; + var session = await loadJsonSessionFile(); + final MyApp myApp = MyApp( initialRoute: initialRoute, + session: session //context: localContext, ); @@ -31,8 +36,9 @@ Future 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 { visualDensity: VisualDensity.adaptivePlatformDensity, ), routes: { - '/welcome': (context) => LoginScreen(), + '/welcome': (context) => LoginScreen(session: widget.session), '/main': (context) => MainScreen() } ), @@ -68,3 +74,9 @@ class _MyAppState extends State { } } +Future loadJsonSessionFile() async { + Session session = await SessionHelper().readSession(); + print(session); + return session; +} + diff --git a/pubspec.lock b/pubspec.lock index 4afd60c..0e60b01 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index a37d4dd..fb22ec9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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