Random generation of xkcd-style passwords now functioning as expected, web interface now serves randomly generated xkcd-style passwords and provides a button to create a sharing link for them when a new generation is requested. Misc stylesheet updates. Generalized the hx-vals helper function in Core.Templates to be useful for arbitrary endpoints that will need to include specific JSON. Added configuration field for dbSalt which will be used as an encryption salt in the next commit when passwods are stored encrypted in the DB instead of in plaintext.

This commit is contained in:
James Eversole 2022-07-29 17:40:45 -05:00
parent b1a4251d95
commit bbe315c450
15 changed files with 142 additions and 29 deletions

View File

@ -24,9 +24,11 @@ library
Core.SQLite Core.SQLite
Core.Templates Core.Templates
Core.Types Core.Types
Feature.Generation.HTTP
Feature.Generation.Links Feature.Generation.Links
Feature.Generation.Passwords Feature.Generation.Passwords
Feature.Generation.Shared Feature.Generation.Shared
Feature.Generation.Templates
Feature.Sharing.HTTP Feature.Sharing.HTTP
Feature.Sharing.SQLite Feature.Sharing.SQLite
Feature.Sharing.Templates Feature.Sharing.Templates

View File

@ -10,4 +10,6 @@
, applicationHost = "REPLACEME" , applicationHost = "REPLACEME"
, applicationPort = +3000 , applicationPort = +3000
, dbFile = "data/Purr.sqlite" , dbFile = "data/Purr.sqlite"
, dbSalt = "REPLACEME!!!!!"
, linkLength = +24
} }

View File

@ -2,8 +2,9 @@ module Core.HTTP ( app ) where
import Core.Types import Core.Types
import Core.Templates (renderIndex, renderStyle) import Core.Templates (renderIndex, renderStyle)
import Feature.Sharing.HTTP as Sharing import Feature.Sharing.HTTP as Sharing
import Feature.Generation.HTTP as Generation
import Data.Maybe (Maybe (Nothing)) import Data.Maybe (Maybe (Nothing))
import Network.Wai.Middleware.RequestLogger (logStdoutDev) import Network.Wai.Middleware.RequestLogger (logStdoutDev)
@ -26,3 +27,4 @@ app = do
-- Feature Routes -- Feature Routes
Sharing.routes Sharing.routes
Generation.routes

View File

@ -1,7 +1,7 @@
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
module Core.Templates ( renderIndex, renderStyle ) where module Core.Templates ( renderIndex, renderStyle, hxVals ) where
import Text.Blaze.Html.Renderer.Text (renderHtml) import Text.Blaze.Html.Renderer.Text (renderHtml)
import Text.Blaze.Html import Text.Blaze.Html
@ -16,10 +16,10 @@ import Prelude
renderIndex :: String -> LT.Text renderIndex :: String -> LT.Text
renderIndex link = renderHtml ( $(shamletFile "./views/index.hamlet") ) renderIndex link = renderHtml ( $(shamletFile "./views/index.hamlet") )
where where
hsUserLink = userLinkAttr link hsUserLink = hxVals "userLink" link
renderStyle :: LT.Text renderStyle :: LT.Text
renderStyle = renderCss ( $(cassiusFile "./views/cassius/style.cassius") "/style.css" ) renderStyle = renderCss ( $(cassiusFile "./views/cassius/style.cassius") "/style.css" )
userLinkAttr :: String -> (String, String) hxVals :: String -> String -> (String, String)
userLinkAttr str = ("hx-vals", "{\"userLink\": \"" <> str <> "\"}") hxVals attr str = ("hx-vals", "{\"" <> attr <> "\": \"" <> str <> "\"}")

View File

@ -20,5 +20,6 @@ data DhallConfig = DhallConfig
, applicationHost :: String , applicationHost :: String
, applicationPort :: Int , applicationPort :: Int
, dbFile :: String , dbFile :: String
, dbSalt :: String
, linkLength :: Int , linkLength :: Int
} deriving (Generic, Show) } deriving (Generic, Show)

View File

@ -0,0 +1,22 @@
module Feature.Generation.HTTP ( routes ) where
import Core.Types
import Core.Templates (renderIndex)
import Feature.Generation.Passwords (suggestedScheme)
import Feature.Generation.Templates (renderGen)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Control.Monad.Reader (ask, lift, liftIO)
import Data.Maybe (listToMaybe)
import Web.Scotty.Trans
import Prelude
routes :: PurrApp ()
routes = do
get "/gen" $ do
genPw <- liftIO $ suggestedScheme 24
html $ renderGen genPw

View File

@ -1,7 +1,7 @@
module Feature.Generation.Links ( genLink ) where module Feature.Generation.Links ( genLink ) where
import Core.Types import Core.Types
import Feature.Generation.Shared (rIndex) import Feature.Generation.Shared (rIndex, validChars)
import Control.Monad.Reader (ask, lift, liftIO) import Control.Monad.Reader (ask, lift, liftIO)
import Data.Char (toLower, toUpper) import Data.Char (toLower, toUpper)
@ -21,12 +21,6 @@ genLink = do
fin <- liftIO $ randCapitalization res fin <- liftIO $ randCapitalization res
genLink' (d - 1) (cs <> (fin:[])) genLink' (d - 1) (cs <> (fin:[]))
-- Defines the valid range of characters to be used when generating links.
-- This consists of all lowercase Latin alphabet characters and the
-- numbers 1 through 9.
validChars :: [Char]
validChars = ['a'..'z'] <> ['1'..'9']
randChar :: IO Char randChar :: IO Char
randChar = rIndex validChars randChar = rIndex validChars

View File

@ -1,25 +1,47 @@
module Feature.Generation.Passwords where module Feature.Generation.Passwords where
import Core.Types import Core.Types
import Feature.Generation.Shared (rIndex) import Feature.Generation.Shared (rIndex, validChars)
import Control.Monad.Reader (ask, lift, liftIO) import Control.Monad.Reader (ask, lift, liftIO)
import Data.Char (toLower, toUpper) import Data.Char (toLower, toUpper)
import Data.List (singleton)
import System.IO import System.IO
import System.Random import System.Random
camelCase :: [Char] -> [Char]
camelCase [] = []
camelCase x = toUpper (head x) : map toLower (tail x)
-- suggestedScheme and its helpers xkcd, oldschool, and gibberish are TODO. -- suggestedScheme and its helpers xkcd, oldschool, and gibberish are TODO.
suggestedScheme :: Int -> String suggestedScheme :: Int -> IO String
suggestedScheme i suggestedScheme i
| i > 23 = xkcd i | i > 23 = xkcd i
| i > 12 = oldschool i | i > 12 = oldschool i
| otherwise = gibberish i | otherwise = gibberish i
xkcd :: Int -> String xkcd :: Int -> IO String
xkcd i = take i "correcthorsebatterystaple" xkcd i = do
wOne <- randomCamel
wTwo <- randomCamel
wThree <- randomCamel
wFour <- randomCamel
return $ wOne <> wTwo <> wThree <> wFour
oldschool :: Int -> String oldschool :: Int -> IO String
oldschool i = take i "PowerProlonger2974!" oldschool i = do
wOne <- randomWord
wTwo <- randomWord
return $ wOne <> wTwo
gibberish :: Int -> String gibberish :: Int -> IO String
gibberish i = take i "TCYx#@z5zlgw1o" gibberish i = return "mf98sgs7bgg%#"
wordList :: IO [String]
wordList = fmap lines (readFile "./src/Feature/Generation/wordlist.txt")
randomWord :: IO String
randomWord = wordList >>= rIndex
randomCamel :: IO String
randomCamel = camelCase <$> randomWord

View File

@ -6,3 +6,15 @@ rIndex :: [a] -> IO a
rIndex arr = do rIndex arr = do
i <- randomRIO (0, length arr - 1) i <- randomRIO (0, length arr - 1)
return $ arr !! i return $ arr !! i
-- Defines the valid range of characters to be used when generating.
-- This consists of all lowercase Latin alphabet characters and the
-- numbers 1 through 9.
validChars :: [Char]
validChars = validLetters <> validNumbers
validNumbers :: [Char]
validNumbers = ['1'..'9']
validLetters :: [Char]
validLetters = ['a'..'z']

View File

@ -0,0 +1,20 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
module Feature.Generation.Templates ( renderGen ) where
import Core.Templates (hxVals)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Text.Blaze.Html
import Text.Hamlet (shamletFile)
import Prelude
renderGen :: String -> LT.Text
renderGen genPw = renderHtml ( $(shamletFile "./views/gen.hamlet") )
where
hsGeneratedSharing = hxVals "newSec" genPw

View File

@ -195,6 +195,7 @@ bury
bush bush
busy busy
butter butter
butterfly
button button
buyer buyer
cable cable
@ -605,7 +606,6 @@ hall
hammer hammer
hand hand
handle handle
hang
happen happen
happy happy
hard hard
@ -614,7 +614,6 @@ harm
hate hate
hatred hatred
have have
head
heal heal
health health
hear hear
@ -628,7 +627,6 @@ help
hence hence
here here
hero hero
hers
hide hide
high high
highly highly

View File

@ -4,14 +4,18 @@
@colorFour: #435F5D @colorFour: #435F5D
html html
font-family: Courier
background-color: #{colorTwo} background-color: #{colorTwo}
color: #{colorOne} color: #{colorOne}
body body
font-family: Courier
font-size: 20px font-size: 20px
text-align: left text-align: left
h2
font-family: monaco, Consolas, monospace
text-transform: uppercase
p p
margin: 0.4em 0 0.4em 0 margin: 0.4em 0 0.4em 0
@ -38,6 +42,7 @@ a
height: 1% height: 1%
.mainButton .mainButton
margin: 0 0 0.25em 0
padding: 0.75em 1.75em padding: 0.75em 1.75em
background-color: #{colorThree} background-color: #{colorThree}
color: #{colorTwo} color: #{colorTwo}
@ -51,8 +56,8 @@ a
outline: none outline: none
color: #{colorOne} color: #{colorOne}
background: #{colorTwo} background: #{colorTwo}
margin: 1em 0 margin: 0.5em 0
border-style: none none none none border-style: none none solid none
padding: 0.4em 0 padding: 0.4em 0
box-sizing: border-box box-sizing: border-box
-webkit-box-sizing: border-box -webkit-box-sizing: border-box
@ -77,6 +82,9 @@ a
.pwUtils .pwUtils
width: 75% width: 75%
.generators
margin: 5% 0 0 0
.shareNew .shareNew
margin-bottom: 2em margin-bottom: 2em

19
views/gen.hamlet Normal file
View File

@ -0,0 +1,19 @@
<div #generators .generators>
<h2>Generators
<p>Generated password:
<h3>#{genPw}
<button .mainButton
hx-get="/gen"
hx-target="#generators"
hx-swap="outerHTML"
/>
Generate New Password
<br />
<button .mainButton
hx-post="/new"
hx-target="#requestedPw"
hx-swap="outerHTML"
*{hsGeneratedSharing}
/>
Share Generated Password
<img class="htmx-indicator" src="/loading.svg" />

View File

@ -18,6 +18,7 @@ $doctype 5
<a #titleLink .titleLink href="/">Purr <a #titleLink .titleLink href="/">Purr
<div #pwUtils .pwUtils> <div #pwUtils .pwUtils>
<h2>Sharing Tools
$if (link == "/") $if (link == "/")
<div #requestedPw .requestedPw> <div #requestedPw .requestedPw>
<p .emptyReq> <p .emptyReq>
@ -62,3 +63,12 @@ $doctype 5
/> />
Get Secret Get Secret
<img class="htmx-indicator" src="/loading.svg" /> <img class="htmx-indicator" src="/loading.svg" />
<div #generators .generators>
<h2>Generators
<button .mainButton
hx-get="/gen"
hx-target="#generators"
hx-swap="outerHTML"
/>
Generate Password

View File

@ -1,6 +1,7 @@
<div #requestedPw .requestedPw> <div #requestedPw .requestedPw>
$maybe pw <- password $maybe pw <- password
<p>Here's the secret found at <a href="/pw/#{link}">/pw/#{link}</a>: <p>Here's the secret found at <a href="/pw/#{link}">/pw/#{link}</a>:
<h2 .pwResult>#{pw} <h3 .pwResult>#{pw}
<hr />
$nothing $nothing
<p>No secret found at <a href="/pw/#{link}">/pw/#{link}</a> <h3>No secret found at <a href="/pw/#{link}">/pw/#{link}</a>