mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-10-31 18:24:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			152 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:comunic/helpers/server_config_helper.dart';
 | |
| import 'package:comunic/models/server_config.dart';
 | |
| import 'package:comunic/utils/intl_utils.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| 
 | |
| /// New password input widget
 | |
| ///
 | |
| /// @author Pierre Hubert
 | |
| 
 | |
| class UserInfoForPassword {
 | |
|   final String firstName;
 | |
|   final String lastName;
 | |
|   final String email;
 | |
| 
 | |
|   const UserInfoForPassword({
 | |
|     @required this.firstName,
 | |
|     @required this.lastName,
 | |
|     @required this.email,
 | |
|   });
 | |
| }
 | |
| 
 | |
| class NewPasswordInputWidget extends StatefulWidget {
 | |
|   final Widget icon;
 | |
|   final VoidCallback onEdited;
 | |
|   final VoidCallback onSubmitted;
 | |
|   final TextInputAction textInputAction;
 | |
|   final String label;
 | |
| 
 | |
|   final UserInfoForPassword user;
 | |
| 
 | |
|   const NewPasswordInputWidget({
 | |
|     Key key,
 | |
|     this.icon,
 | |
|     this.onEdited,
 | |
|     this.onSubmitted,
 | |
|     this.textInputAction,
 | |
|     @required this.label,
 | |
|     @required this.user,
 | |
|   })  : assert(label != null),
 | |
|         assert(user != null),
 | |
|         super(key: key);
 | |
| 
 | |
|   @override
 | |
|   NewPasswordInputWidgetState createState() => NewPasswordInputWidgetState();
 | |
| }
 | |
| 
 | |
| class NewPasswordInputWidgetState extends State<NewPasswordInputWidget> {
 | |
|   final TextEditingController _controller = TextEditingController();
 | |
| 
 | |
|   String get value => _controller.text;
 | |
| 
 | |
|   bool get valid => value.isNotEmpty && (_errorMessage ?? "").isEmpty;
 | |
| 
 | |
|   PasswordPolicy get _policy => ServerConfigurationHelper.config.passwordPolicy;
 | |
| 
 | |
|   @override
 | |
|   void didUpdateWidget(covariant NewPasswordInputWidget oldWidget) {
 | |
|     super.didUpdateWidget(oldWidget);
 | |
|     setState(() {});
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) => TextField(
 | |
|         controller: _controller,
 | |
|         obscureText: true,
 | |
|         onChanged: (s) => _onChanged(),
 | |
|         onSubmitted:
 | |
|             widget.onSubmitted == null ? null : (s) => widget.onSubmitted(),
 | |
|         textInputAction: widget.textInputAction,
 | |
|         decoration: InputDecoration(
 | |
|           errorText: _errorMessage,
 | |
|           errorMaxLines: 3,
 | |
|           icon: widget.icon,
 | |
|           labelText: widget.label,
 | |
|         ),
 | |
|       );
 | |
| 
 | |
|   void _onChanged() {
 | |
|     setState(() {});
 | |
|     if (widget.onEdited != null) widget.onEdited();
 | |
|   }
 | |
| 
 | |
|   /// Generate an error message associated with current password
 | |
|   String get _errorMessage {
 | |
|     if (value.isEmpty) return null;
 | |
| 
 | |
|     // Mandatory checks
 | |
|     if (!_policy.allowMailInPassword &&
 | |
|         (widget.user.email ?? "").isNotEmpty &&
 | |
|         (widget.user.email.toLowerCase().contains(value.toLowerCase()) ||
 | |
|             value.toLowerCase().contains(widget.user.email.toLowerCase()))) {
 | |
|       return tr("Your password must not contains part of your email address!");
 | |
|     }
 | |
| 
 | |
|     if (!_policy.allowNameInPassword &&
 | |
|         (widget.user.firstName ?? "").isNotEmpty &&
 | |
|         value.toLowerCase().contains(widget.user.firstName.toLowerCase())) {
 | |
|       return tr("Your password must not contains your first name!");
 | |
|     }
 | |
| 
 | |
|     if (!_policy.allowNameInPassword &&
 | |
|         (widget.user.lastName ?? "").isNotEmpty &&
 | |
|         value.toLowerCase().contains(widget.user.lastName.toLowerCase())) {
 | |
|       return tr("Your password must not contains your last name!");
 | |
|     }
 | |
| 
 | |
|     if (_policy.minPasswordLength > value.length) {
 | |
|       return tr(
 | |
|         "Your password must be composed of at least %num% characters!",
 | |
|         args: {
 | |
|           "num": _policy.minPasswordLength.toString(),
 | |
|         },
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     // Characteristics check
 | |
|     var count = 0;
 | |
| 
 | |
|     if (_hasCharacteristic(RegExp(r'[A-Z]'), _policy.minNumberUpperCaseLetters))
 | |
|       count++;
 | |
| 
 | |
|     if (_hasCharacteristic(RegExp(r'[a-z]'), _policy.minNumberLowerCaseLetters))
 | |
|       count++;
 | |
| 
 | |
|     if (_hasCharacteristic(RegExp(r'[0-9]'), _policy.minNumberDigits)) count++;
 | |
| 
 | |
|     if (_hasCharacteristic(
 | |
|         RegExp(r'[^A-Za-z0-9]'), _policy.minNumberSpecialCharacters)) count++;
 | |
| 
 | |
|     if (count >= _policy.minCategoriesPresence) return null;
 | |
| 
 | |
|     return tr(
 | |
|       "Your password must contains characters of at least %num% of the following categories : %upper% upper case letter, %lower% lowercase letter, %digit% digit, %special% special character.",
 | |
|       args: {
 | |
|         "num": (_policy.minCategoriesPresence == 4
 | |
|             ? tr("ALL")
 | |
|             : _policy.minCategoriesPresence.toString()),
 | |
|         "upper": _policy.minNumberUpperCaseLetters.toString(),
 | |
|         "lower": _policy.minNumberLowerCaseLetters.toString(),
 | |
|         "digit": _policy.minNumberDigits.toString(),
 | |
|         "special": _policy.minNumberSpecialCharacters.toString(),
 | |
|       },
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   bool _hasCharacteristic(RegExp exp, int requiredCount) {
 | |
|     if (requiredCount < 1) return true;
 | |
| 
 | |
|     return exp.allMatches(value).length >= requiredCount;
 | |
|   }
 | |
| }
 |