Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
5.7 KiB

  1. import 'dart:convert';
  2. import 'dart:typed_data';
  3. import 'package:http/http.dart' as http;
  4. import 'package:flutter/material.dart';
  5. import 'package:pointycastle/impl.dart';
  6. import '/components/flash_message.dart';
  7. import '/components/custom_title_bar.dart';
  8. import '/models/my_profile.dart';
  9. import '/utils/encryption/aes_helper.dart';
  10. import '/utils/encryption/crypto_utils.dart';
  11. import '/utils/storage/session_cookie.dart';
  12. @immutable
  13. class ChangePassword extends StatelessWidget {
  14. ChangePassword({
  15. Key? key,
  16. required this.privateKey
  17. }) : super(key: key);
  18. final RSAPrivateKey privateKey;
  19. final _formKey = GlobalKey<FormState>();
  20. final TextEditingController _currentPasswordController = TextEditingController();
  21. final TextEditingController _newPasswordController = TextEditingController();
  22. final TextEditingController _newPasswordConfirmController = TextEditingController();
  23. bool invalidCurrentPassword = false;
  24. @override
  25. Widget build(BuildContext context) {
  26. return Scaffold(
  27. appBar: const CustomTitleBar(
  28. title: Text(
  29. 'Profile',
  30. style: TextStyle(
  31. fontSize: 32,
  32. fontWeight: FontWeight.bold
  33. )
  34. ),
  35. showBack: true,
  36. backgroundColor: Colors.transparent,
  37. ),
  38. body: Form(
  39. key: _formKey,
  40. child: Padding(
  41. padding: const EdgeInsets.only(
  42. left: 20,
  43. right: 20,
  44. top: 30,
  45. ),
  46. child: Column(
  47. crossAxisAlignment: CrossAxisAlignment.center,
  48. children: [
  49. const Text(
  50. 'Change Password',
  51. style: TextStyle(
  52. fontSize: 25,
  53. ),
  54. ),
  55. const SizedBox(height: 30),
  56. TextFormField(
  57. controller: _currentPasswordController,
  58. decoration: const InputDecoration(
  59. hintText: 'Current Password',
  60. ),
  61. // The validator receives the text that the user has entered.
  62. validator: (value) {
  63. if (value == null || value.isEmpty) {
  64. return 'Enter your current password';
  65. }
  66. if (invalidCurrentPassword) {
  67. return 'Invalid password';
  68. }
  69. return null;
  70. },
  71. ),
  72. const SizedBox(height: 10),
  73. TextFormField(
  74. controller: _newPasswordController,
  75. obscureText: true,
  76. enableSuggestions: false,
  77. autocorrect: false,
  78. decoration: const InputDecoration(
  79. hintText: 'New Password',
  80. ),
  81. // The validator receives the text that the user has entered.
  82. validator: (value) {
  83. if (value == null || value.isEmpty) {
  84. return 'Enter a new password';
  85. }
  86. return null;
  87. },
  88. ),
  89. const SizedBox(height: 10),
  90. TextFormField(
  91. controller: _newPasswordConfirmController,
  92. obscureText: true,
  93. enableSuggestions: false,
  94. autocorrect: false,
  95. decoration: const InputDecoration(
  96. hintText: 'Confirm Password',
  97. ),
  98. // The validator receives the text that the user has entered.
  99. validator: (value) {
  100. if (value == null || value.isEmpty) {
  101. return 'Confirm your password';
  102. }
  103. if (value != _newPasswordController.text) {
  104. return 'Passwords do not match';
  105. }
  106. return null;
  107. },
  108. ),
  109. const SizedBox(height: 15),
  110. ElevatedButton(
  111. onPressed: () {
  112. if (!_formKey.currentState!.validate()) {
  113. return;
  114. }
  115. ScaffoldMessenger.of(context).showSnackBar(
  116. const SnackBar(content: Text('Processing Data')),
  117. );
  118. _changePassword(context)
  119. .then((dynamic) {
  120. Navigator.of(context).pop();
  121. });
  122. },
  123. child: const Text('Submit'),
  124. ),
  125. ],
  126. )
  127. )
  128. )
  129. );
  130. }
  131. Future<void> _changePassword(BuildContext context) async {
  132. String privateKeyPem = CryptoUtils.encodeRSAPrivateKeyToPem(privateKey);
  133. String privateKeyEncrypted = AesHelper.aesEncrypt(
  134. _newPasswordController.text,
  135. Uint8List.fromList(privateKeyPem.codeUnits),
  136. );
  137. String payload = jsonEncode({
  138. 'old_password': _currentPasswordController.text,
  139. 'new_password': _newPasswordController.text,
  140. 'new_password_confirm': _newPasswordConfirmController.text,
  141. 'private_key': privateKeyEncrypted,
  142. });
  143. var resp = await http.post(
  144. await MyProfile.getServerUrl('api/v1/auth/change_password'),
  145. headers: <String, String>{
  146. 'Content-Type': 'application/json; charset=UTF-8',
  147. 'cookie': await getSessionCookie(),
  148. },
  149. body: payload,
  150. );
  151. if (resp.statusCode == 403) {
  152. invalidCurrentPassword = true;
  153. _formKey.currentState!.validate();
  154. return;
  155. }
  156. if (resp.statusCode != 204) {
  157. showMessage(
  158. 'An unexpected error occured, please try again later.',
  159. context,
  160. );
  161. }
  162. }
  163. }