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
conn <- liftIO $ open db
res <- liftIO $ query conn "SELECT * from pws WHERE link = ?" (Only link)
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)
liftIO $ close conn
liftIO $ execute conn
"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

@ -10,9 +10,12 @@ import Database.SQLite.Simple.FromRow
import qualified Data.Text as T
data SecretEntry = SecretEntry
{ link :: T.Text
, secret :: T.Text
, date :: Integer
{ 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

@ -36,14 +36,33 @@ $doctype 5
<p>
<input .mainInput
name="newSec"
type="text"
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" />