From 4eb2468cef24c296894020de286d4e21baec188c Mon Sep 17 00:00:00 2001 From: chasevill <98123650+rhit-villencr@users.noreply.github.com> Date: Fri, 10 Jan 2025 23:21:37 -0500 Subject: [PATCH] Login Page with registering new user. Salting Added. Account Page still not done yet. Tests Written. Handle different users + account setting page #33 Co-authored-by: rhit-norflwe --- app/accountSetting.tsx | 61 ++++++++++----- app/home.tsx | 2 +- backend/omniplanner.db | Bin 118784 -> 118784 bytes backend/omniplanner/build.gradle | 1 + backend/omniplanner/omniplanner.db | Bin 118784 -> 118784 bytes .../omniplanner/OmniplannerApplication.java | 3 +- .../com/main/omniplanner/SecurityConfig.java | 14 ++++ .../omniplanner/user/PasswordService.java | 34 +++++++++ .../java/com/main/omniplanner/user/User.java | 24 +++--- .../main/omniplanner/user/UserController.java | 2 +- .../main/omniplanner/user/UserRepository.java | 2 + .../main/omniplanner/user/UserService.java | 22 +++++- .../UserTests/PasswordServiceTest.java | 72 ++++++++++++++++++ .../UserTests/UserServiceTest.java | 14 +++- .../main/omniplanner/UserTests/UserTest.java | 6 +- 15 files changed, 216 insertions(+), 41 deletions(-) create mode 100644 backend/omniplanner/src/main/java/com/main/omniplanner/SecurityConfig.java create mode 100644 backend/omniplanner/src/main/java/com/main/omniplanner/user/PasswordService.java create mode 100644 backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/PasswordServiceTest.java diff --git a/app/accountSetting.tsx b/app/accountSetting.tsx index e5af9a4..dc29b8f 100644 --- a/app/accountSetting.tsx +++ b/app/accountSetting.tsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native'; -// import { cLog } from './log'; -import { IPAddr } from './constants'; -import axios from 'axios'; +// import { IPAddr } from './constants'; +// import axios from 'axios'; import { cLog } from './log'; +import { useFocusEffect } from '@react-navigation/native'; +import AsyncStorage from '@react-native-async-storage/async-storage'; // import { useFocusEffect } from '@react-navigation/native'; // import GenericViewPageForm from './viewEventPage'; @@ -14,18 +15,13 @@ export default function AccountSetting () { const handleSave = async () => { try { - // Add current date to formData - const currentDate = new Date().toISOString().split('T')[0]; // Get current date in ISO format (e.g., 2024-06-14T10:00:00.000Z) - const currentTime = new Date().toTimeString().split(' ')[0]; - const updatedFormData = { - ...formData, - event_date: currentDate, // Add date field - event_time: currentTime, // Add time field - }; - const hit = IPAddr + '/add_note'; // Backend endpoint - const response = await axios.put(hit, updatedFormData); // Send request with updated data - - cLog('Note saved successfully: ' + response.data); + cLog(formData); + await AsyncStorage.setItem('name', formData.name); + await AsyncStorage.setItem('userName', formData.userName); + await AsyncStorage.setItem('email', formData.email); + // const hit = IPAddr + '/modify_user'; // Backend endpoint + // const response = await axios.put(hit, formData); // Send request with updated data + // cLog('Note saved successfully: ' + response.data); } catch (error) { console.error('Error saving note:', error); } @@ -35,13 +31,40 @@ export default function AccountSetting () { setFormData({ ...formData, [name]: value }); }; + // Fetch local data from AsyncStorage + const getLocalValues = async () => { + try { + const userId = await AsyncStorage.getItem('userId'); + const name = await AsyncStorage.getItem('name'); + const userName = await AsyncStorage.getItem('userName'); + const email = await AsyncStorage.getItem('email'); + + // Update state with retrieved values + setFormData({ + userId: userId ? parseInt(userId, 10) : 1, // Default to 1 if not found + name: name || '', + userName: userName || '', + email: email || '', + }); + } catch (error) { + console.error("Error fetching local values from AsyncStorage:", error); + } + }; + + + useFocusEffect( + useCallback(() => { + getLocalValues(); + }, []) + ); + return ( Name handleChange("text", text)} // Handle text changes + onChangeText={(text) => handleChange("name", text)} // Handle text changes style={styles.textInput} placeholder="name" multiline={true} @@ -49,7 +72,7 @@ export default function AccountSetting () { Username handleChange("text", text)} // Handle text changes + onChangeText={(text) => handleChange("userName", text)} // Handle text changes style={styles.textInput} placeholder="username" multiline={true} @@ -57,7 +80,7 @@ export default function AccountSetting () { Email handleChange("text", text)} // Handle text changes + onChangeText={(text) => handleChange("email", text)} // Handle text changes style={styles.textInput} placeholder="email" multiline={true} diff --git a/app/home.tsx b/app/home.tsx index 1f61d83..3e5e7b3 100644 --- a/app/home.tsx +++ b/app/home.tsx @@ -59,7 +59,7 @@ export default function TaskScreen() { return ( pYmbF#M3RSq0@8Bp3nTM85obEPc>S_Hok=NH(hF`kG*yTxcES~;y2s0>yPc%r6N9Y?BUOhpLFI=$rl zTyD6tMdBeM4<%a_w$du3sCoVMqM>H8etO8#J%8>oUD>rw3YUx}ZFNT!oi=N}fg((? zDSA9PD2NTDXe)kD*Se(9nTVwVjlUEJ`CRVrF?)cryCq|PNN;2q+r!8_iMWWwcT{%9 zpsOyyWwOQ~Ws<3-TdxEJzo1Lwo?axDdCSF41&>8Kl zpx7cP#nZ>fB-PP8pfEEqx5TM3D8JsLET_WMKRm*t#MCX>)q;tGL7Ra?S5-W@EHfv^ zAsyxx=ZwVSRE030SZZEsX<2GPVsUYKeo=~{k+BI_sR1JggFXX?tg1^&x#X9-|r3gD?=;9tPEoX?lngQa4-&;mwtmdOeN`dp3GLhRz= c;*8Cqlb^9{ozAh8QGfCS9-i$*OBwsL0DI4J6#xJL diff --git a/backend/omniplanner/build.gradle b/backend/omniplanner/build.gradle index 53fb9a2..ad6983e 100644 --- a/backend/omniplanner/build.gradle +++ b/backend/omniplanner/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'com.google.api-client:google-api-client:2.0.0' implementation 'com.google.oauth-client:google-oauth-client-jetty:1.34.1' implementation 'com.google.apis:google-api-services-calendar:v3-rev20220715-2.0.0' + implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.mockito:mockito-core:5.3.1' diff --git a/backend/omniplanner/omniplanner.db b/backend/omniplanner/omniplanner.db index 37499591a967cb625122718b8dc9750e7baddc51..e78dd58551b487b38eac603e6888c9a1cf1ce7ac 100644 GIT binary patch delta 120 zcmZozz}~QceS);0G6MsHED*zh>_i=7M&-tYtqF|F<_jEQ;GfGkh0lc7n&%<+71kr$ z#TGEGW94en5@Ht@7iVlSp3c9TaqIL=%NX_L6N`(>^NUjAGZKq46v`5dk~0#EG>lA5 RHFXq<6LU(oUj!=B1OP5|BCG%a delta 97 zcmZozz}~QceS);0A_D`1ED*zh^h6zFM#aX2tqF|F<_mmg;GfGkh0lc7n&%<+71qz& v#TGEGW1XxZpwHD { @Query("SELECT u FROM User u WHERE u.userName = :userName") List findByUserName(String userName); + + User save(User user); } diff --git a/backend/omniplanner/src/main/java/com/main/omniplanner/user/UserService.java b/backend/omniplanner/src/main/java/com/main/omniplanner/user/UserService.java index 43afb59..1008d1b 100644 --- a/backend/omniplanner/src/main/java/com/main/omniplanner/user/UserService.java +++ b/backend/omniplanner/src/main/java/com/main/omniplanner/user/UserService.java @@ -12,9 +12,13 @@ public class UserService { @Autowired private UserRepository userRepository; + @Autowired + private PasswordService passwordService; + // Constructor injection - public UserService(UserRepository userRepository) { + public UserService(UserRepository userRepository, PasswordService passwordService){ this.userRepository = userRepository; + this.passwordService = passwordService; } public List getAllUsers() { @@ -23,13 +27,16 @@ public List getAllUsers() { public String login(String username, String password) { List users = userRepository.findByUserName(username); + if (users.isEmpty()) { System.out.println("No users found with username: " + username); - return null; + User newUser = new User(); + newUser.setUserName(username); + return registerUser(newUser, password)+"," + username + "," + null + "," + null; } for (User user : users) { - if (user.getPassword().equals(password)) { + if (passwordService.verifyPassword(password, user.getSalt(), user.getPasswordHash())) { return user.getId() + "," + user.getUserName() + "," + user.getEmail() + "," + user.getName(); } } @@ -37,5 +44,14 @@ public String login(String username, String password) { System.out.println("No matching password for username: " + username); return null; } + + public Integer registerUser(User user, String password) { + String salt = passwordService.generateSalt(); + String hashedPassword = passwordService.hashPassword(password, salt); + user.setSalt(salt); + user.setPasswordHash(hashedPassword);// Ensure the ID is null for new users + userRepository.save(user); + return user.getId(); + } } diff --git a/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/PasswordServiceTest.java b/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/PasswordServiceTest.java new file mode 100644 index 0000000..b260119 --- /dev/null +++ b/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/PasswordServiceTest.java @@ -0,0 +1,72 @@ +package com.main.omniplanner.UserTests; + +import com.main.omniplanner.user.PasswordService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class PasswordServiceTest { + + @Mock + private BCryptPasswordEncoder passwordEncoder; + + private PasswordService passwordService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + passwordService = new PasswordService(passwordEncoder); + } + + @Test + public void testGenerateSaltLength() { + String salt = passwordService.generateSalt(); + assertEquals(24, salt.length()); + } + + @Test + public void testGenerateSaltUnique() { + String salt1 = passwordService.generateSalt(); + String salt2 = passwordService.generateSalt(); + assertNotEquals(salt1, salt2); + } + + @Test + public void hashPasswordLength() { + String password = "mySecurePassword"; + String salt = "randomSalt"; + + String hashedPassword = "hashedPasswordExample"; + when(passwordEncoder.encode(password + salt)).thenReturn(hashedPassword); + + assertEquals(hashedPassword, passwordService.hashPassword(password, salt)); + } + + @Test + public void hashPasswordNotSame() { + String password = "mySecurePassword"; + String salt = "randomSalt"; + + when(passwordEncoder.encode(password + salt)).thenReturn("hashedPassword1", "hashedPassword2"); + + assertNotEquals(passwordService.hashPassword(password, salt), passwordService.hashPassword(password, salt)); + } + + @Test + public void verifyPassword() { + String password = "mySecurePassword"; + String salt = "randomSalt"; + String hashedPassword = "hashedPassword"; + + when(passwordEncoder.matches(password + salt, hashedPassword)).thenReturn(true); + + assertTrue(passwordService.verifyPassword(password, salt, hashedPassword)); + + verify(passwordEncoder).matches(password + salt, hashedPassword); + } +} diff --git a/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserServiceTest.java b/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserServiceTest.java index ea21007..5372951 100644 --- a/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserServiceTest.java +++ b/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserServiceTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import com.main.omniplanner.user.PasswordService; import com.main.omniplanner.user.User; import com.main.omniplanner.user.UserRepository; import com.main.omniplanner.user.UserService; @@ -22,19 +23,23 @@ public class UserServiceTest { @Mock private UserRepository userRepository; + @Mock + private PasswordService passwordService; + private User user; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); - userService = new UserService(userRepository); + userService = new UserService(userRepository, passwordService); user = new User(); user.setId(0); user.setName("John Doe"); user.setEmail("johndoe@example.com"); user.setGoogle_calendar_linked(true); user.setGoogle_calendar_access_token("access_token_123"); - user.setPassword("password123"); + user.setPasswordHash("passwordhash123"); + user.setSalt("salt123"); } @Test @@ -55,7 +60,7 @@ public void testGetSaveEvent() { @Test public void testLoginSuccess() { when(userRepository.findByUserName("johndoe")).thenReturn(Arrays.asList(user)); - + when(passwordService.verifyPassword("password123", user.getSalt(), user.getPasswordHash())).thenReturn(true); String result = userService.login("johndoe", "password123"); assertNotNull(result); @@ -65,6 +70,7 @@ public void testLoginSuccess() { @Test public void testLoginFailure() { when(userRepository.findByUserName("johndoe")).thenReturn(Arrays.asList(user)); + when(passwordService.verifyPassword("wrongpassword", user.getSalt(), user.getPasswordHash())).thenReturn(false); String result = userService.login("johndoe", "wrongpassword"); @@ -77,6 +83,6 @@ public void testUserNotFound() { String result = userService.login("nonexistent", "password123"); - assertNull(result); + assertEquals(result, "0,nonexistent,null,null"); } } diff --git a/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserTest.java b/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserTest.java index f615885..8758ae4 100644 --- a/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserTest.java +++ b/backend/omniplanner/src/test/java/com/main/omniplanner/UserTests/UserTest.java @@ -54,8 +54,8 @@ public void testGetSetUserName() { user.setUserName("johndoe"); assertEquals("johndoe", user.getUserName()); } - @Test public void testGetSetPassword() { - user.setPassword("password123"); - assertEquals("password123", user.getPassword()); + @Test public void testGetSetPasswordHash() { + user.setPasswordHash("password123"); + assertEquals("password123", user.getPasswordHash()); } }