Skip to content

Commit eedcc79

Browse files
committed
Feature: New password change (Temp) added
Due to azure down SLCM switched to this
1 parent 4d05f04 commit eedcc79

File tree

2 files changed

+289
-0
lines changed

2 files changed

+289
-0
lines changed

lib/change_password.dart

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:http/http.dart' as http;
3+
import 'package:html/parser.dart' show parse;
4+
5+
class ChangePasswordPage extends StatefulWidget {
6+
final String sessionCookie;
7+
const ChangePasswordPage({super.key, required this.sessionCookie});
8+
9+
@override
10+
State<ChangePasswordPage> createState() => _ChangePasswordPageState();
11+
}
12+
13+
class _ChangePasswordPageState extends State<ChangePasswordPage> {
14+
final TextEditingController newPassController = TextEditingController();
15+
final TextEditingController confirmPassController = TextEditingController();
16+
17+
bool _isLoading = false;
18+
bool _newPassVisible = false;
19+
bool _confirmPassVisible = false;
20+
String _strengthFeedback = "";
21+
Color _feedbackColor = Colors.red;
22+
23+
@override
24+
void initState() {
25+
super.initState();
26+
newPassController.addListener(_updatePasswordFeedback);
27+
}
28+
29+
void _updatePasswordFeedback() {
30+
final password = newPassController.text;
31+
final result = validatePasswordStrength(password);
32+
33+
setState(() {
34+
if (password.isEmpty) {
35+
_strengthFeedback = "";
36+
} else if (result['strength'] == 5) {
37+
_strengthFeedback = "Strong password";
38+
_feedbackColor = Colors.green;
39+
} else {
40+
_strengthFeedback =
41+
"Weak: ${(result['feedback'] as List<String>).join(', ')}";
42+
_feedbackColor = Colors.red;
43+
}
44+
});
45+
}
46+
47+
void _showError(String message) {
48+
ScaffoldMessenger.of(context).showSnackBar(
49+
SnackBar(
50+
content: Text(message, style: const TextStyle(color: Colors.white)),
51+
backgroundColor: Colors.red,
52+
),
53+
);
54+
}
55+
56+
Future<void> _handleSubmit() async {
57+
final newPass = newPassController.text;
58+
final confirmPass = confirmPassController.text;
59+
60+
final result = validatePasswordStrength(newPass);
61+
62+
if (newPass.isEmpty || confirmPass.isEmpty) {
63+
_showError("Both fields are required.");
64+
return;
65+
}
66+
67+
if (newPass != confirmPass) {
68+
_showError("Passwords do not match.");
69+
return;
70+
}
71+
72+
if (result['strength'] != 5) {
73+
_showError("Password does not meet all strength requirements.");
74+
return;
75+
}
76+
77+
setState(() => _isLoading = true);
78+
final success = await changePassword(widget.sessionCookie, newPass);
79+
setState(() => _isLoading = false);
80+
81+
if (success && mounted) {
82+
Navigator.pushReplacementNamed(context, 'login');
83+
} else {
84+
_showError("Failed to change password.");
85+
}
86+
}
87+
88+
Future<bool> changePassword(String cookies, String newPassword) async {
89+
const url = "https://mujslcm.jaipur.manipal.edu/Home/ChangePassword";
90+
final session = http.Client();
91+
92+
final headers = {
93+
"User-Agent":
94+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
95+
"Cookie": cookies,
96+
};
97+
98+
final response = await session.get(Uri.parse(url), headers: headers);
99+
if (response.statusCode != 200) return false;
100+
101+
final document = parse(response.body);
102+
final token = document
103+
.querySelector('input[name="__RequestVerificationToken"]')
104+
?.attributes['value'];
105+
106+
if (token == null) return false;
107+
108+
final payload = {
109+
"__RequestVerificationToken": token,
110+
"newPassword": newPassword,
111+
"confirmPassword": newPassword,
112+
};
113+
114+
final result =
115+
await session.post(Uri.parse(url), headers: headers, body: payload);
116+
117+
return result.statusCode == 302 && result.headers['location'] == "/";
118+
}
119+
120+
Map<String, dynamic> validatePasswordStrength(String password) {
121+
int strength = 0;
122+
List<String> feedback = [];
123+
124+
if (password.length >= 8) {
125+
strength++;
126+
} else {
127+
feedback.add("At least 8 characters");
128+
}
129+
130+
if (RegExp(r'[A-Z]').hasMatch(password)) {
131+
strength++;
132+
} else {
133+
feedback.add("One uppercase letter");
134+
}
135+
136+
if (RegExp(r'[a-z]').hasMatch(password)) {
137+
strength++;
138+
} else {
139+
feedback.add("One lowercase letter");
140+
}
141+
142+
if (RegExp(r'\d').hasMatch(password)) {
143+
strength++;
144+
} else {
145+
feedback.add("One number");
146+
}
147+
148+
if (RegExp(r'[@$!%*?&]').hasMatch(password)) {
149+
strength++;
150+
} else {
151+
feedback.add("One special character (@\$!%*?&)");
152+
}
153+
154+
return {"strength": strength, "feedback": feedback};
155+
}
156+
157+
@override
158+
void dispose() {
159+
newPassController.dispose();
160+
confirmPassController.dispose();
161+
super.dispose();
162+
}
163+
164+
@override
165+
Widget build(BuildContext context) {
166+
return Scaffold(
167+
backgroundColor: const Color(0xFF212121),
168+
body: Center(
169+
child: Padding(
170+
padding: const EdgeInsets.symmetric(horizontal: 24.0),
171+
child: SingleChildScrollView(
172+
child: Column(
173+
crossAxisAlignment: CrossAxisAlignment.start,
174+
children: [
175+
const Text(
176+
'Change Password',
177+
style: TextStyle(
178+
color: Color(0xFFD5E7B5),
179+
fontSize: 30,
180+
fontWeight: FontWeight.bold,
181+
),
182+
),
183+
const SizedBox(height: 30),
184+
TextField(
185+
controller: newPassController,
186+
obscureText: !_newPassVisible,
187+
decoration: InputDecoration(
188+
filled: true,
189+
fillColor: const Color(0xFF24272B),
190+
hintText: 'Enter new password',
191+
hintStyle: const TextStyle(color: Colors.grey),
192+
prefixIcon: const Icon(Icons.lock, color: Colors.grey),
193+
suffixIcon: IconButton(
194+
icon: Icon(
195+
_newPassVisible
196+
? Icons.visibility_off
197+
: Icons.visibility,
198+
color: Colors.grey,
199+
),
200+
onPressed: () {
201+
setState(() {
202+
_newPassVisible = !_newPassVisible;
203+
});
204+
},
205+
),
206+
border: OutlineInputBorder(
207+
borderRadius: BorderRadius.circular(8.0),
208+
borderSide: BorderSide.none,
209+
),
210+
),
211+
style: const TextStyle(color: Colors.white),
212+
),
213+
const SizedBox(height: 8),
214+
Text(
215+
_strengthFeedback,
216+
style: TextStyle(color: _feedbackColor, fontSize: 13),
217+
),
218+
const SizedBox(height: 16),
219+
TextField(
220+
controller: confirmPassController,
221+
obscureText: !_confirmPassVisible,
222+
decoration: InputDecoration(
223+
filled: true,
224+
fillColor: const Color(0xFF24272B),
225+
hintText: 'Confirm new password',
226+
hintStyle: const TextStyle(color: Colors.grey),
227+
prefixIcon:
228+
const Icon(Icons.lock_outline, color: Colors.grey),
229+
suffixIcon: IconButton(
230+
icon: Icon(
231+
_confirmPassVisible
232+
? Icons.visibility_off
233+
: Icons.visibility,
234+
color: Colors.grey,
235+
),
236+
onPressed: () {
237+
setState(() {
238+
_confirmPassVisible = !_confirmPassVisible;
239+
});
240+
},
241+
),
242+
border: OutlineInputBorder(
243+
borderRadius: BorderRadius.circular(8.0),
244+
borderSide: BorderSide.none,
245+
),
246+
),
247+
style: const TextStyle(color: Colors.white),
248+
),
249+
const SizedBox(height: 30),
250+
SizedBox(
251+
width: double.infinity,
252+
child: ElevatedButton(
253+
onPressed: _handleSubmit,
254+
style: ElevatedButton.styleFrom(
255+
backgroundColor: const Color(0xFFD5E7B5),
256+
foregroundColor: Colors.black,
257+
padding: const EdgeInsets.symmetric(vertical: 16),
258+
shape: RoundedRectangleBorder(
259+
borderRadius: BorderRadius.circular(8.0),
260+
),
261+
),
262+
child: _isLoading
263+
? const CircularProgressIndicator(
264+
valueColor: AlwaysStoppedAnimation(Colors.black),
265+
)
266+
: const Text('Submit'),
267+
),
268+
),
269+
],
270+
),
271+
),
272+
),
273+
),
274+
);
275+
}
276+
}

lib/login.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'session_manager.dart';
88
import 'package:local_auth/local_auth.dart';
99
import 'redirects.dart';
1010
import 'dart:async';
11+
import 'change_password.dart';
1112

1213
class MyLogin extends StatefulWidget {
1314
const MyLogin({super.key});
@@ -154,6 +155,18 @@ class _MyLoginState extends State<MyLogin> {
154155
if (loginResponse.statusCode == 302) {
155156
final locationHeader = loginResponse.headers['location'];
156157
if (locationHeader != null) {
158+
if (locationHeader.contains('/Home/ChangePassword')) {
159+
final newCookies =
160+
cleanedCookies + (cleanedAPI.isNotEmpty ? '; $cleanedAPI' : '');
161+
Navigator.pushReplacement(
162+
context,
163+
MaterialPageRoute(
164+
builder: (context) =>
165+
ChangePasswordPage(sessionCookie: newCookies),
166+
),
167+
);
168+
return null;
169+
}
157170
final redirectedUrl = Uri.parse(baseurl + locationHeader);
158171

159172
final newCookies =

0 commit comments

Comments
 (0)