Read encrypted secret entries from the database and decode/unencrypt appropriately, add max duration/view count entries for the database and frontend interface, update README to remove graceful JS degradation goal, remove a few wordlist entries

This commit is contained in:
James Eversole 2022-07-31 17:41:54 -05:00
parent 478384aae9
commit ca73ed7982
8 changed files with 96 additions and 29 deletions

View File

@ -18,7 +18,6 @@ You shouldn't! This is [free and open-source software](https://git.eversole.co/J
## Project Goals
1. Provide a minimal and clean interface for generating and sharing passwords.
2. Maintain a clean and organized codebase that can be extended to include more utilities than originally anticipated.
3. Aim for graceful degradation when JavaScript isn't enabled.
## Development & Support
Please send me an [email](mailto:james@eversole.co) or join the [Support Chat](openpgp4fpr://FEB27223219E8DB3203225350462EA0901FE08F7#a=james%40eversole.co&g=Purr%20Support&x=RbVs8iQCVnf&i=-FuzUDK_RM1&s=KgeGtFFJtkq) in [DeltaChat](https://delta.chat)!

View File

@ -15,7 +15,10 @@ main db = do
"CREATE TABLE IF NOT EXISTS pws\
\ (link TEXT PRIMARY KEY,\
\ secret TEXT,\
\ date DATETIME DEFAULT CURRENT_TIMESTAMP)"
\ date DATETIME DEFAULT CURRENT_TIMESTAMP,\
\ life INT,\
\ views INT,\
\ maxViews INT)"
close conn
dbPath :: PurrAction String

View File

@ -175,7 +175,6 @@ bread
break
breast
breath
breed
brick
bridge
brief
@ -820,9 +819,7 @@ media
medium
meet
melt
member
memory
mental
menu
mere
merely
@ -1035,7 +1032,6 @@ profit
prompt
proof
proper
proud
prove
public
pull

View File

@ -26,10 +26,12 @@ routes = do
post "/pw" $ do
reqId <- param "userLink"
res <- findByLink reqId
html $ renderPw reqId (secret <$> res)
html $ renderPw reqId res
post "/new" $ do
reqSecret <- param "newSec"
reqDur <- param "newSecDuration"
reqViews <- param "newSecViews"
link <- genLink
insertNewSecret reqSecret (T.pack link)
insertNewSecret reqSecret reqDur (T.pack link) reqViews
html $ renderPw link (Just reqSecret)

View File

@ -9,7 +9,6 @@ import Crypto.Simple.CBC (encrypt, decrypt)
import Data.Maybe (listToMaybe)
import Data.Time.Clock.POSIX (getPOSIXTime)
import Database.SQLite.Simple
import Database.SQLite.Simple.FromRow
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as B
@ -17,28 +16,44 @@ import qualified Data.Text as T
import qualified Data.Text.Encoding as ET
import qualified Data.Text.Lazy as LT
findByLink :: String -> PurrAction (Maybe SecretEntry)
findByLink :: String -> PurrAction (Maybe T.Text)
findByLink link = do
db <- dbPath
key <- encKey
conn <- liftIO $ open db
res <- liftIO $ query conn "SELECT * from pws WHERE link = ?" (Only link)
liftIO $ close conn
return $ listToMaybe res
readEncryptedSecret key res
insertNewSecret :: T.Text -> T.Text -> PurrAction ()
insertNewSecret sec link = do
insertNewSecret :: T.Text -> Integer -> T.Text -> Integer -> PurrAction ()
insertNewSecret sec life link maxViews = do
db <- dbPath
key <- encKey
encSec <- liftIO $ encrypt (B.pack key) (ET.encodeUtf8 sec)
encSec <- liftIO $ encryptSecret key sec
conn <- liftIO $ open db
time <- liftIO $ epochTime
liftIO $ execute conn
"INSERT INTO pws (link, secret, date) VALUES (?, ?, ?)"
(SecretEntry link (encodeSecret encSec) time)
"INSERT INTO pws (link, secret, date, life, views, maxViews) VALUES (?, ?, ?, ?, ?, ?)"
(SecretEntry link (encodeSecret encSec) time life 0 maxViews)
liftIO $ close conn
epochTime :: IO Integer
epochTime = fmap round getPOSIXTime
readEncryptedSecret :: String -> [SecretEntry] -> PurrAction (Maybe T.Text)
readEncryptedSecret key sec = do
decKey <- liftIO
(sequence $ decryptSecret key <$> decodeSecret <$> listToMaybe sec)
return (ET.decodeLatin1 <$> decKey)
encodeSecret :: B.ByteString -> T.Text
encodeSecret b = ET.decodeUtf8 $ B64.encode b
decodeSecret :: SecretEntry -> B.ByteString
decodeSecret s = B64.decodeLenient $ ET.encodeUtf8 (secret s)
encryptSecret :: String -> T.Text -> IO B.ByteString
encryptSecret k s = encrypt (B.pack k) (ET.encodeUtf8 s)
decryptSecret :: String -> B.ByteString -> IO B.ByteString
decryptSecret k b = decrypt (B.pack k) b
epochTime :: IO Integer
epochTime = fmap round getPOSIXTime

View File

@ -13,6 +13,9 @@ data SecretEntry = SecretEntry
{ link :: T.Text
, secret :: T.Text
, date :: Integer
, life :: Integer
, views :: Integer
, maxViews :: Integer
} deriving (Show, Generic)
instance FromRow SecretEntry where

View File

@ -56,7 +56,7 @@ a
outline: none
color: #{colorOne}
background: #{colorTwo}
margin: 0.5em 0
margin: 0.5em 0 1em 0
border-style: none none solid none
padding: 0.4em 0
box-sizing: border-box
@ -75,6 +75,33 @@ a
color: #{colorThree}
opacity: 0.5
.numberInput
text-align: center
font-weight: 400
font-size: 1em
width: 10%
outline: none
color: #{colorOne}
background: #{colorTwo}
margin: 0.5em 0
border-style: none none solid none
padding: 0.4em 0
box-sizing: border-box
-webkit-box-sizing: border-box
-moz-box-sizing: border-box
-webkit-transition: all 0.1s ease-in-out
-moz-transition: all 0.1s ease-in-out
-ms-transition: all 0.1s ease-in-out
-o-transition: all 0.1s ease-in-out
transition: all 0.1s ease-in-out
.numberInput:focus
border-bottom: 0.2em solid #{colorThree};
.numberInput:focus::placeholder
color: #{colorThree}
opacity: 0.5
.pwResult
font-size: 1.5em
color: #{colorFour}
@ -109,6 +136,9 @@ a
width: 95%
text-align: center
.numberInput
width: 25%
.title
margin: 8% auto 2% auto
font-size: 3em

View File

@ -39,11 +39,30 @@ $doctype 5
type="text"
placeholder="Enter a Secret to Share"
/>
<br />
Valid for:
<br />
<input .numberInput
name="newSecDuration"
type="number"
min="1"
max="90"
value="20"
onkeyup="if (value < 1 || value > 90) { value = 0 }"
/> days
<input .numberInput
name="newSecViews"
type="number"
min="1"
max="60"
value="20"
onkeyup="if (value < 1 || value > 60) { value = 0 }"
/> views
<button .mainButton
hx-post="/new"
hx-target="#requestedPw"
hx-swap="outerHTML"
hx-include="[name='newSec']"
hx-include="[id='shareNew']"
/>
Share Secret
<img class="htmx-indicator" src="/loading.svg" />