Listing & adding entries now works

This commit is contained in:
s473615 2024-05-25 17:15:52 +02:00
parent d34a957780
commit 7d4c37debe
7 changed files with 154 additions and 34 deletions

View File

@ -41,6 +41,7 @@ library
, ansi-terminal , ansi-terminal
, base >=4.7 && <5 , base >=4.7 && <5
, bytestring , bytestring
, cipher-aes
, hashable , hashable
, sqlite-simple , sqlite-simple
, text , text
@ -62,6 +63,7 @@ executable HasswordManager-exe
, ansi-terminal , ansi-terminal
, base >=4.7 && <5 , base >=4.7 && <5
, bytestring , bytestring
, cipher-aes
, hashable , hashable
, sqlite-simple , sqlite-simple
, text , text
@ -84,6 +86,7 @@ test-suite HasswordManager-test
, ansi-terminal , ansi-terminal
, base >=4.7 && <5 , base >=4.7 && <5
, bytestring , bytestring
, cipher-aes
, hashable , hashable
, sqlite-simple , sqlite-simple
, text , text

View File

@ -1,9 +1,10 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Main (main) where module Main (main) where
import Data.ByteString.UTF8 (fromString) import Data.ByteString.UTF8 (fromString, toString)
import Database.SQLite.Simple import Database.SQLite.Simple
import System.Hclip import System.Hclip
import qualified Data.Text as T
import qualified UserInterface as Ui import qualified UserInterface as Ui
import qualified Database as Db import qualified Database as Db
@ -13,18 +14,19 @@ import qualified Utils as Ut
main :: IO () main :: IO ()
main = do main = do
conn <- Db.init_connection conn <- Db.init_connection
Ut.clear_screen Ut.clear_screen
putStrLn "Welcome to Hassword Manager!!!" putStrLn "Welcome to Hassword Manager!!!"
welcome_screen conn
setup_application conn
mpass <- open_hassword_book conn mpass <- open_hassword_book conn
putStrLn mpass application_loop conn mpass
setClipboard mpass
-- application_loop conn
Db.close_connection conn Db.close_connection conn
welcome_screen :: Connection -> IO ()
welcome_screen conn = do setup_application :: Connection -> IO ()
setup_application conn = do
first <- Db.is_it_first_app_usage conn first <- Db.is_it_first_app_usage conn
if first if first
then do then do
@ -66,8 +68,8 @@ open_hassword_book :: Connection -> IO (String)
open_hassword_book conn = do open_hassword_book conn = do
putStrLn "Please Enter your MASTER PASSWORD:" putStrLn "Please Enter your MASTER PASSWORD:"
mpass <- Ut.get_password mpass <- Ut.get_password
res <- Db.check_if_mpass_valid conn (Cr.hash' $ fromString mpass) mpass_valid <- Db.check_if_mpass_valid conn (Cr.hash' $ fromString mpass)
if res if mpass_valid
then then
return mpass return mpass
else do else do
@ -76,7 +78,74 @@ open_hassword_book conn = do
Ut.reset_color Ut.reset_color
open_hassword_book conn open_hassword_book conn
application_loop :: Connection -> IO () get_action_choice :: IO (Ut.ActionChoice)
application_loop conn = do get_action_choice = do
putStrLn "What would you like to do?" putStrLn "\nWhat would you like to do?"
application_loop conn putStrLn "1. List all entries"
putStrLn "2. Copy entry password"
putStrLn "3. Add a new entry"
putStrLn "4. Delete an entry"
putStrLn "5. Update an entry"
putStrLn "6. Change Master Password"
putStrLn "7. Close the application"
choice <- getLine
case choice of
"1" -> return Ut.ListAllEntries
"2" -> return Ut.CopyEntryPassword
"3" -> return Ut.AddNewEntry
"4" -> return Ut.DeleteEntry
"5" -> return Ut.UpdateEntry
"6" -> return Ut.ChangeMasterPassword
"7" -> return Ut.Exit
_ -> return Ut.InvalidAction
application_loop :: Connection -> String -> IO ()
application_loop conn mpass = do
choice <- get_action_choice
case choice of
Ut.ListAllEntries -> do
list_all_entries conn
application_loop conn mpass
Ut.CopyEntryPassword -> do
return ()
application_loop conn mpass
Ut.AddNewEntry -> do
add_entry conn mpass
application_loop conn mpass
Ut.DeleteEntry -> do
return ()
application_loop conn mpass
Ut.UpdateEntry -> do
return ()
application_loop conn mpass
Ut.ChangeMasterPassword -> do
return ()
application_loop conn mpass
Ut.InvalidAction -> do
putStrLn "Invalid choice!!!"
application_loop conn mpass
Ut.Exit -> do
return ()
list_all_entries :: Connection -> IO ()
list_all_entries conn = do
entries <- Db.get_all_entries conn
Ui.print_entries entries
add_entry :: Connection -> String -> IO ()
add_entry conn mpass = do
putStrLn "Please Enter the service name:"
service <- getLine
putStrLn "Please Enter the login:"
login <- getLine
entry_exists <- Db.entry_already_exists conn (T.pack service) (T.pack login)
if entry_exists
then do
Ut.set_red
putStrLn "Entry already exists!!!"
Ut.reset_color
return ()
else do
putStrLn "Please Enter the password:"
password <- Ut.get_password
Db.add_entry conn (T.pack service) (T.pack login) (T.pack (toString (Cr.encrypt' (fromString mpass) (fromString password))))

View File

@ -27,6 +27,7 @@ dependencies:
- utf8-string - utf8-string
- ansi-terminal - ansi-terminal
- Hclip - Hclip
- cipher-aes
- base >= 4.7 && < 5 - base >= 4.7 && < 5
ghc-options: ghc-options:

View File

@ -1,15 +1,28 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Crypto ( module Crypto (
-- encode, encrypt',
-- decode, decrypt',
hash' hash'
) where ) where
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.Hashable import Data.Hashable
import Crypto.Cipher.AES
pepper :: ByteString pepper :: ByteString
pepper = "pepper" pepper = "pepper"
hash' :: ByteString -> Int hash' :: ByteString -> Int
hash' = hashWithSalt 0 . mappend pepper hash' = hashWithSalt 0 . mappend pepper
encrypt' :: ByteString -> ByteString -> ByteString
encrypt' key plain_text = plain_text
decrypt' :: ByteString -> ByteString -> ByteString
decrypt' key cypher_text = cypher_text
-- encrypt' :: ByteString -> ByteString -> ByteString
-- encrypt' = encryptECB . initAES
-- decrypt' :: ByteString -> ByteString -> ByteString
-- decrypt' = decryptECB . initAES

View File

@ -1,29 +1,22 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Database ( module Database where
Entry(..),
init_connection,
close_connection,
check_if_mpass_valid,
insert_new_mpass,
is_it_first_app_usage
) where
import Control.Applicative import Control.Applicative
import qualified Data.Text as T import qualified Data.Text as T
import Database.SQLite.Simple import Database.SQLite.Simple
data Entry = Entry { entryId :: Int, entryLogin :: T.Text, entryPassword :: T.Text } deriving (Show) data Entry = Entry { entryId :: Int, entryService :: T.Text, entryLogin :: T.Text, entryPassword :: T.Text } deriving (Show)
data Mpass = Mpass { mpassId :: Int, hash :: Int } deriving (Show) data Mpass = Mpass { mpassId :: Int, hash :: Int } deriving (Show)
instance FromRow Entry where instance FromRow Entry where
fromRow = Entry <$> field <*> field <*> field fromRow = Entry <$> field <*> field <*> field <*> field
instance FromRow Mpass where instance FromRow Mpass where
fromRow = Mpass <$> field <*> field fromRow = Mpass <$> field <*> field
instance ToRow Entry where instance ToRow Entry where
toRow (Entry id_ login password) = toRow (id_, login, password) toRow (Entry id_ service login password) = toRow (id_, service, login, password)
instance ToRow Mpass where instance ToRow Mpass where
toRow (Mpass id_ hash) = toRow (id_, hash) toRow (Mpass id_ hash) = toRow (id_, hash)
@ -41,9 +34,9 @@ close_connection conn = close conn
create_db :: Connection -> IO () create_db :: Connection -> IO ()
create_db conn = do create_db conn = do
execute_ conn "CREATE TABLE IF NOT EXISTS hasswords (entryId INTEGER PRIMARY KEY, entryLogin TEXT, entryPassword TEXT)" execute_ conn "CREATE TABLE IF NOT EXISTS entries (entryId INTEGER PRIMARY KEY AUTOINCREMENT, entryService TEXT, entryLogin TEXT, entryPassword TEXT)"
execute_ conn "CREATE TABLE IF NOT EXISTS mpass (mpassId INTEGER PRIMARY KEY, hash INTEGER)" execute_ conn "CREATE TABLE IF NOT EXISTS mpass (mpassId INTEGER PRIMARY KEY AUTOINCREMENT, hash INTEGER)"
-- execute conn "INSERT INTO hassword (entryId, entryLogin, entryPassword) VALUES (?,?,?)" (Entry 1 "admin" "admin") -- execute conn "INSERT INTO entries (entryId, entryLogin, entryPassword) VALUES (?,?,?)" (Entry 1 "admin" "admin")
-- rowId <- lastInsertRowId conn -- rowId <- lastInsertRowId conn
-- r <- query_ conn "SELECT * from hassword" :: IO [Entry] -- r <- query_ conn "SELECT * from hassword" :: IO [Entry]
-- mapM_ print r -- mapM_ print r
@ -56,9 +49,36 @@ is_it_first_app_usage conn = do
insert_new_mpass :: Connection -> Int -> IO () insert_new_mpass :: Connection -> Int -> IO ()
insert_new_mpass conn hash = do insert_new_mpass conn hash = do
execute_ conn "DELETE FROM mpass" execute_ conn "DELETE FROM mpass"
execute conn "INSERT INTO mpass (mpassId, hash) VALUES (?,?)" (Mpass 1 hash) execute conn "INSERT INTO mpass (hash) VALUES (?)" (Only hash)
check_if_mpass_valid :: Connection -> Int -> IO Bool check_if_mpass_valid :: Connection -> Int -> IO Bool
check_if_mpass_valid conn hash = do check_if_mpass_valid conn hash = do
r <- query conn "SELECT * from mpass WHERE hash = ?" (Only hash) :: IO [Mpass] r <- query conn "SELECT * from mpass WHERE hash = ?" (Only hash) :: IO [Mpass]
return $ length r == 1 return $ length r == 1
add_entry :: Connection -> T.Text -> T.Text -> T.Text -> IO ()
add_entry conn service login password = do
execute conn "INSERT INTO entries (entryService, entryLogin, entryPassword) VALUES (?,?,?)" (service, login, password)
update_entry :: Connection -> Int -> T.Text -> IO ()
update_entry conn id new_password = do
execute conn "UPDATE entries SET entryPassword = ? WHERE entryId = ?" (new_password, id)
get_entry :: Connection -> Int -> IO (Maybe Entry)
get_entry conn id = do
r <- query conn "SELECT * from entries WHERE entryId = ?" (Only id) :: IO [Entry]
return $ case r of
[] -> Nothing
[entry] -> Just entry
get_all_entries :: Connection -> IO [Entry]
get_all_entries conn = query_ conn "SELECT * from entries"
delete_entry :: Connection -> Int -> IO ()
delete_entry conn id = do
execute conn "DELETE FROM entries WHERE entryId = ?" (Only id)
entry_already_exists :: Connection -> T.Text -> T.Text -> IO Bool
entry_already_exists conn service login = do
r <- query conn "SELECT * from entries WHERE entryService = ? AND entryLogin = ?" (service, login) :: IO [Entry]
return $ length r == 1

View File

@ -1,6 +1,18 @@
module UserInterface module UserInterface
( someFunc ( print_entries
) where ) where
someFunc :: IO () import qualified Database as Db
someFunc = putStrLn "someFunc"
print_entries :: [Db.Entry] -> IO ()
print_entries entries = do
putStrLn "Entries:"
mapM_ print_entry entries
print_entry :: Db.Entry -> IO ()
print_entry entry = do
putStrLn $ "ID: " ++ show (Db.entryId entry)
putStrLn $ "Service: " ++ show (Db.entryService entry)
putStrLn $ "Login: " ++ show (Db.entryLogin entry)
putStrLn $ "Password: *****"
putStrLn ""

View File

@ -6,6 +6,8 @@ import Control.Exception
data MasterPasswordValidationCases = Empty | TooShort | DoNotMatch | NotValid | Valid String deriving (Show) data MasterPasswordValidationCases = Empty | TooShort | DoNotMatch | NotValid | Valid String deriving (Show)
data ActionChoice = ListAllEntries | CopyEntryPassword | AddNewEntry | DeleteEntry | UpdateEntry | ChangeMasterPassword | Exit | InvalidAction deriving (Show)
validate_password :: String -> String -> MasterPasswordValidationCases validate_password :: String -> String -> MasterPasswordValidationCases
validate_password p1 p2 validate_password p1 p2
| p1 /= p2 = DoNotMatch | p1 /= p2 = DoNotMatch