commit ca73ed7982881f8df02d6c8ca0b5df37475c41a4
parent 478384aae90fa4d605ac2141dc46af4d173a285b
Author: James Eversole <james@eversole.co>
Date: Sun, 31 Jul 2022 17:41:54 -0500
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
Diffstat:
8 files changed, 96 insertions(+), 29 deletions(-)
diff --git a/README.md b/README.md
@@ -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)!
diff --git a/src/Core/SQLite.hs b/src/Core/SQLite.hs
@@ -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
diff --git a/src/Feature/Generation/wordlist.txt b/src/Feature/Generation/wordlist.txt
@@ -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
diff --git a/src/Feature/Sharing/HTTP.hs b/src/Feature/Sharing/HTTP.hs
@@ -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)
diff --git a/src/Feature/Sharing/SQLite.hs b/src/Feature/Sharing/SQLite.hs
@@ -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
diff --git a/src/Feature/Sharing/Types.hs b/src/Feature/Sharing/Types.hs
@@ -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
diff --git a/views/cassius/style.cassius b/views/cassius/style.cassius
@@ -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
diff --git a/views/index.hamlet b/views/index.hamlet
@@ -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" />