Skip to content

Commit

Permalink
Login Page with registering new user.
Browse files Browse the repository at this point in the history
Salting Added.
Account Page still not done yet.
Tests Written.
Handle different users + account setting page #33
Co-authored-by: rhit-norflwe <rhit-norflwe@users.noreply.github.com>
  • Loading branch information
rhit-villencr committed Jan 11, 2025
1 parent 6de0168 commit 094afa9
Show file tree
Hide file tree
Showing 15 changed files with 213 additions and 44 deletions.
61 changes: 42 additions & 19 deletions app/accountSetting.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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);
}
Expand All @@ -35,29 +31,56 @@ 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 (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Name</Text>
<TextInput
value={formData.name} // Set value to formData.text to make it controlled
onChangeText={(text) => handleChange("text", text)} // Handle text changes
onChangeText={(text) => handleChange("name", text)} // Handle text changes
style={styles.textInput}
placeholder="name"
multiline={true}
/>
<Text style={styles.title}>Username</Text>
<TextInput
value={formData.userName} // Set value to formData.text to make it controlled
onChangeText={(text) => handleChange("text", text)} // Handle text changes
onChangeText={(text) => handleChange("userName", text)} // Handle text changes
style={styles.textInput}
placeholder="username"
multiline={true}
/>
<Text style={styles.title}>Email</Text>
<TextInput
value={formData.email} // Set value to formData.text to make it controlled
onChangeText={(text) => handleChange("text", text)} // Handle text changes
onChangeText={(text) => handleChange("email", text)} // Handle text changes
style={styles.textInput}
placeholder="email"
multiline={true}
Expand Down
2 changes: 1 addition & 1 deletion app/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function TaskScreen() {
return (
<GenericMainPageForm
title='Home'
header={`Welcome ${header ?? 'User'}!`}
header={`Welcome ${header && header !== 'null' ? header : 'User'}!`}
nextPage='home'
thisPage='home'
tasks={tasks}
Expand Down
Binary file modified backend/omniplanner.db
Binary file not shown.
1 change: 1 addition & 0 deletions backend/omniplanner/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Binary file modified backend/omniplanner/omniplanner.db
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;

@SpringBootApplication
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class }, scanBasePackages = "com.main.omniplanner")
public class OmniplannerApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
package com.main.omniplanner;public class SecurityConfig {
package com.main.omniplanner;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfig {

@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,34 @@
package com.main.omniplanner.user;public class PasswordService {
package com.main.omniplanner.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.util.Base64;

@Service
public class PasswordService {

private final BCryptPasswordEncoder passwordEncoder;

@Autowired
public PasswordService(BCryptPasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}

public String generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}

public String hashPassword(String password, String salt) {
return passwordEncoder.encode(password + salt);
}

public boolean verifyPassword(String password, String salt, String hashedPassword) {
return passwordEncoder.matches(password + salt, hashedPassword);
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package com.main.omniplanner.user;


import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String email;
private boolean google_calendar_linked;
private String google_calendar_access_token;
private String userName;
private String password;
private String passwordHash;
private String salt;

public int getId() {
return id;
}

public void setId(int id) {
public void setId(Integer id) {
this.id = id;
}

Expand Down Expand Up @@ -64,10 +64,16 @@ public String getUserName() {
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
public String getPasswordHash() {
return passwordHash;
}
public void setPassword(String password) {
this.password = password;
public void setPasswordHash(String passwordHash) {
this.passwordHash = passwordHash;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import java.util.List;

@RestController
@CrossOrigin
@CrossOrigin(origins = "*")
public class UserController {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface UserRepository extends JpaRepository<User, Integer> {

@Query("SELECT u FROM User u WHERE u.userName = :userName")
List<User> findByUserName(String userName);

User save(User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<User> getAllUsers() {
Expand All @@ -23,19 +27,31 @@ public List<User> getAllUsers() {

public String login(String username, String password) {
List<User> 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();
}
}

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();
}
}

Original file line number Diff line number Diff line change
@@ -1,2 +1,72 @@
package com.main.omniplanner.UserTests;public class PasswordServiceTest {
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);
}
}
Loading

0 comments on commit 094afa9

Please sign in to comment.