finished product without tests yet
This commit is contained in:
parent
5599850c59
commit
673a455ada
@ -1 +1,8 @@
|
|||||||
# HasswordManager
|
# HasswordManager
|
||||||
|
A simple password manager that uses a master password to encrypt and decrypt passwords. \
|
||||||
|
The master password is hashed with salt and pepper. \
|
||||||
|
The passwords are stored encrypted in sqlite3 database and are decrypted only when needed.
|
||||||
|
|
||||||
|
## Authors:
|
||||||
|
- Jan Wojciechowski
|
||||||
|
- Sebastian Jerzykiewicz
|
||||||
|
114
app/Main.hs
114
app/Main.hs
@ -51,7 +51,7 @@ create_mpass conn = do
|
|||||||
create_mpass conn
|
create_mpass conn
|
||||||
Ut.TooShort -> do
|
Ut.TooShort -> do
|
||||||
Ut.set_red
|
Ut.set_red
|
||||||
putStrLn "Password is too short!!!\n"
|
putStrLn "Password is too short (min 8 symbols)!!!\n"
|
||||||
Ut.reset_color
|
Ut.reset_color
|
||||||
create_mpass conn
|
create_mpass conn
|
||||||
Ut.Empty -> do
|
Ut.Empty -> do
|
||||||
@ -62,7 +62,7 @@ create_mpass conn = do
|
|||||||
|
|
||||||
input_new_valid_mpass :: IO (Ut.MasterPasswordValidationCases)
|
input_new_valid_mpass :: IO (Ut.MasterPasswordValidationCases)
|
||||||
input_new_valid_mpass = do
|
input_new_valid_mpass = do
|
||||||
(password1:password2:_) <- sequence [putStrLn "Please Enter new MASTER PASSWORD:" >> Ut.get_password, putStrLn "Confirm password:" >> Ut.get_password]
|
(password1:password2:_) <- sequence [putStrLn "Please Enter new MASTER PASSWORD:" >> Ut.get_password, putStrLn "Enter the password again:" >> Ut.get_password]
|
||||||
return $ Ut.validate_password password1 password2
|
return $ Ut.validate_password password1 password2
|
||||||
|
|
||||||
open_hassword_book :: Connection -> IO (String)
|
open_hassword_book :: Connection -> IO (String)
|
||||||
@ -83,10 +83,10 @@ get_action_choice :: IO (Ut.ActionChoice)
|
|||||||
get_action_choice = do
|
get_action_choice = do
|
||||||
putStrLn "\nWhat would you like to do?"
|
putStrLn "\nWhat would you like to do?"
|
||||||
putStrLn "1. List all entries"
|
putStrLn "1. List all entries"
|
||||||
putStrLn "2. Copy entry password"
|
putStrLn "2. Copy an entry password"
|
||||||
putStrLn "3. Add a new entry"
|
putStrLn "3. Add a new entry"
|
||||||
putStrLn "4. Delete an entry"
|
putStrLn "4. Delete an entry"
|
||||||
putStrLn "5. Update an entry"
|
putStrLn "5. Update an entry password"
|
||||||
putStrLn "6. Change Master Password"
|
putStrLn "6. Change Master Password"
|
||||||
putStrLn "7. Close the application"
|
putStrLn "7. Close the application"
|
||||||
choice <- getLine
|
choice <- getLine
|
||||||
@ -105,25 +105,64 @@ application_loop conn mpass = do
|
|||||||
choice <- get_action_choice
|
choice <- get_action_choice
|
||||||
case choice of
|
case choice of
|
||||||
Ut.ListAllEntries -> do
|
Ut.ListAllEntries -> do
|
||||||
|
entries <- Db.get_all_entries conn
|
||||||
|
if null entries
|
||||||
|
then do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "No entries to list!!!"
|
||||||
|
Ut.reset_color
|
||||||
|
application_loop conn mpass
|
||||||
|
else do
|
||||||
list_all_entries conn
|
list_all_entries conn
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.CopyEntryPassword -> do
|
Ut.CopyEntryPassword -> do
|
||||||
|
entries <- Db.get_all_entries conn
|
||||||
|
if null entries
|
||||||
|
then do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "No entries to copy a password from!!!"
|
||||||
|
Ut.reset_color
|
||||||
|
application_loop conn mpass
|
||||||
|
else do
|
||||||
copy_entry_pass conn mpass
|
copy_entry_pass conn mpass
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.AddNewEntry -> do
|
Ut.AddNewEntry -> do
|
||||||
add_entry conn mpass
|
add_entry conn mpass
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.DeleteEntry -> do
|
Ut.DeleteEntry -> do
|
||||||
|
entries <- Db.get_all_entries conn
|
||||||
|
if null entries
|
||||||
|
then do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "No entries to delete!!!"
|
||||||
|
Ut.reset_color
|
||||||
|
application_loop conn mpass
|
||||||
|
else do
|
||||||
delete_entry conn
|
delete_entry conn
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.UpdateEntry -> do
|
Ut.UpdateEntry -> do
|
||||||
|
entries <- Db.get_all_entries conn
|
||||||
|
if null entries
|
||||||
|
then do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "No entries to update!!!"
|
||||||
|
Ut.reset_color
|
||||||
|
application_loop conn mpass
|
||||||
|
else do
|
||||||
update_entry conn mpass
|
update_entry conn mpass
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.ChangeMasterPassword -> do
|
Ut.ChangeMasterPassword -> do
|
||||||
return ()
|
mpass <- change_mpass conn mpass
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.InvalidAction -> do
|
Ut.InvalidAction -> do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
putStrLn "Invalid choice!!!"
|
putStrLn "Invalid choice!!!"
|
||||||
|
Ut.reset_color
|
||||||
application_loop conn mpass
|
application_loop conn mpass
|
||||||
Ut.Exit -> do
|
Ut.Exit -> do
|
||||||
return ()
|
return ()
|
||||||
@ -145,10 +184,12 @@ copy_entry_pass conn mpass = do
|
|||||||
Just (service, login) -> do
|
Just (service, login) -> do
|
||||||
entry <- Db.get_entry conn (T.pack service) (T.pack login)
|
entry <- Db.get_entry conn (T.pack service) (T.pack login)
|
||||||
setClipboard (Cr.decrypt' mpass (T.unpack $ Db.entryPassword entry))
|
setClipboard (Cr.decrypt' mpass (T.unpack $ Db.entryPassword entry))
|
||||||
|
Ut.clear_screen
|
||||||
Ut.set_green
|
Ut.set_green
|
||||||
putStrLn "Password copied to clipboard!!!"
|
putStrLn "Password copied to clipboard!!!"
|
||||||
Ut.reset_color
|
Ut.reset_color
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
Ut.clear_screen
|
||||||
Ut.set_red
|
Ut.set_red
|
||||||
putStrLn "Invalid entry ID!!!"
|
putStrLn "Invalid entry ID!!!"
|
||||||
Ut.reset_color
|
Ut.reset_color
|
||||||
@ -156,13 +197,15 @@ copy_entry_pass conn mpass = do
|
|||||||
|
|
||||||
add_entry :: Connection -> String -> IO ()
|
add_entry :: Connection -> String -> IO ()
|
||||||
add_entry conn mpass = do
|
add_entry conn mpass = do
|
||||||
putStrLn "Please Enter the service name:"
|
Ut.clear_screen
|
||||||
|
putStrLn "Please Enter the service name for the new entry:"
|
||||||
service <- getLine
|
service <- getLine
|
||||||
putStrLn "Please Enter the login:"
|
putStrLn "Please Enter the login for the new entry:"
|
||||||
login <- getLine
|
login <- getLine
|
||||||
entry_exists <- Db.entry_already_exists conn (T.pack service) (T.pack login)
|
entry_exists <- Db.entry_already_exists conn (T.pack service) (T.pack login)
|
||||||
if entry_exists
|
if entry_exists
|
||||||
then do
|
then do
|
||||||
|
Ut.clear_screen
|
||||||
Ut.set_red
|
Ut.set_red
|
||||||
putStrLn "Entry already exists!!!"
|
putStrLn "Entry already exists!!!"
|
||||||
Ut.reset_color
|
Ut.reset_color
|
||||||
@ -171,6 +214,10 @@ add_entry conn mpass = do
|
|||||||
putStrLn "Please Enter the password:"
|
putStrLn "Please Enter the password:"
|
||||||
password <- Ut.get_password
|
password <- Ut.get_password
|
||||||
Db.add_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
|
Db.add_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_green
|
||||||
|
putStrLn "Entry added successfully!!!"
|
||||||
|
Ut.reset_color
|
||||||
|
|
||||||
update_entry :: Connection -> String -> IO ()
|
update_entry :: Connection -> String -> IO ()
|
||||||
update_entry conn mpass = do
|
update_entry conn mpass = do
|
||||||
@ -183,7 +230,12 @@ update_entry conn mpass = do
|
|||||||
putStrLn "Please Enter the new password:"
|
putStrLn "Please Enter the new password:"
|
||||||
password <- Ut.get_password
|
password <- Ut.get_password
|
||||||
Db.update_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
|
Db.update_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_green
|
||||||
|
putStrLn "Entry updated successfully!!!"
|
||||||
|
Ut.reset_color
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
Ut.clear_screen
|
||||||
Ut.set_red
|
Ut.set_red
|
||||||
putStrLn "Invalid entry ID!!!"
|
putStrLn "Invalid entry ID!!!"
|
||||||
Ut.reset_color
|
Ut.reset_color
|
||||||
@ -197,8 +249,56 @@ delete_entry conn = do
|
|||||||
case result of
|
case result of
|
||||||
Just (service, login) -> do
|
Just (service, login) -> do
|
||||||
Db.delete_entry conn (T.pack service) (T.pack login)
|
Db.delete_entry conn (T.pack service) (T.pack login)
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_green
|
||||||
|
putStrLn "Entry deleted successfully!!!"
|
||||||
|
Ut.reset_color
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
Ut.clear_screen
|
||||||
Ut.set_red
|
Ut.set_red
|
||||||
putStrLn "Invalid entry ID!!!"
|
putStrLn "Invalid entry ID!!!"
|
||||||
Ut.reset_color
|
Ut.reset_color
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
change_mpass :: Connection -> String -> IO (String)
|
||||||
|
change_mpass conn mpass = do
|
||||||
|
putStrLn "Please Enter your current MASTER PASSWORD:"
|
||||||
|
current_mpass <- Ut.get_password
|
||||||
|
mpass_valid <- Db.check_if_mpass_valid conn (Cr.hash' $ fromString current_mpass)
|
||||||
|
if mpass_valid
|
||||||
|
then do
|
||||||
|
res <- input_new_valid_mpass
|
||||||
|
case res of
|
||||||
|
Ut.Valid new_mpass -> do
|
||||||
|
Db.insert_new_mpass conn (Cr.hash' $ fromString new_mpass)
|
||||||
|
entries <- Db.get_all_entries conn
|
||||||
|
mapM_ (\entry -> Db.update_entry conn (Db.entryService entry) (Db.entryLogin entry) (T.pack (Cr.encrypt' new_mpass (Cr.decrypt' mpass (T.unpack (Db.entryPassword entry)))))) entries
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_green
|
||||||
|
putStrLn "Master Password changed successfully!!!\n"
|
||||||
|
Ut.reset_color
|
||||||
|
return new_mpass
|
||||||
|
Ut.DoNotMatch -> do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "Passwords do not match!!!\n"
|
||||||
|
Ut.reset_color
|
||||||
|
return mpass
|
||||||
|
Ut.TooShort -> do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "Password is too short (min 8 symbols)!!!\n"
|
||||||
|
Ut.reset_color
|
||||||
|
return mpass
|
||||||
|
Ut.Empty -> do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "Password cannot be empty!!!\n"
|
||||||
|
Ut.reset_color
|
||||||
|
return mpass
|
||||||
|
else do
|
||||||
|
Ut.clear_screen
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "Invalid MASTER PASSWORD!!!\n"
|
||||||
|
Ut.reset_color
|
||||||
|
return mpass
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
module UserInterface where
|
module UserInterface where
|
||||||
|
|
||||||
import qualified Database as Db
|
import qualified Database as Db
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Utils as Ut
|
||||||
|
import Text.Read (readMaybe)
|
||||||
|
|
||||||
print_entries :: [Db.Entry] -> IO ()
|
print_entries :: [Db.Entry] -> IO ()
|
||||||
print_entries entries = do
|
print_entries entries = do
|
||||||
@ -9,10 +12,18 @@ print_entries entries = do
|
|||||||
|
|
||||||
print_entry :: (Int, Db.Entry) -> IO ()
|
print_entry :: (Int, Db.Entry) -> IO ()
|
||||||
print_entry (num, entry) = do
|
print_entry (num, entry) = do
|
||||||
putStrLn $ show num ++ " " ++ show (Db.entryService entry) ++ " " ++ show (Db.entryLogin entry) ++ " " ++ "********"
|
putStrLn $ show num ++ ". " ++ T.unpack (Db.entryService entry) ++ " " ++ T.unpack (Db.entryLogin entry) ++ " " ++ "********"
|
||||||
|
|
||||||
choose_entry :: String -> IO Int
|
choose_entry :: String -> IO Int
|
||||||
choose_entry command = do
|
choose_entry command = do
|
||||||
putStrLn $ command
|
putStrLn command
|
||||||
entry_id <- getLine
|
entry_id <- getLine
|
||||||
return (read entry_id :: Int)
|
let res = readMaybe entry_id :: Maybe Int
|
||||||
|
case res of
|
||||||
|
Just x -> return x
|
||||||
|
Nothing -> do
|
||||||
|
Ut.set_red
|
||||||
|
putStrLn "Invalid entry ID!!!"
|
||||||
|
Ut.reset_color
|
||||||
|
choose_entry command
|
||||||
|
|
@ -20,7 +20,8 @@ validate_password p1 p2
|
|||||||
| otherwise = Valid p1
|
| otherwise = Valid p1
|
||||||
|
|
||||||
clear_screen :: IO ()
|
clear_screen :: IO ()
|
||||||
clear_screen = clearScreen
|
clear_screen = do
|
||||||
|
clearFromCursorToScreenBeginning
|
||||||
|
|
||||||
set_red :: IO ()
|
set_red :: IO ()
|
||||||
set_red = setSGR [SetColor Foreground Vivid Red]
|
set_red = setSGR [SetColor Foreground Vivid Red]
|
||||||
|
12
test/Spec.hs
12
test/Spec.hs
@ -1,2 +1,12 @@
|
|||||||
|
import Test.Hspec
|
||||||
|
import qualified Crypto as Cr
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = putStrLn "Test suite not yet implemented"
|
main = hspec $ do
|
||||||
|
describe "Crypto" $ do
|
||||||
|
it "should encrypt and decrypt a message correctly" $ do
|
||||||
|
let originalMessage = "Hello, World!"
|
||||||
|
masterPassword = "mysecretpassword"
|
||||||
|
encryptedMessage = Cr.encrypt' masterPassword originalMessage
|
||||||
|
decryptedMessage = Cr.decrypt' masterPassword encryptedMessage
|
||||||
|
decryptedMessage `shouldBe` originalMessage
|
||||||
|
Loading…
Reference in New Issue
Block a user