PBD7LZYQHXAA3KLH2ZUX5GW4UFML6BQ32KXZF4KZ6OYFASUYFJ5QC
I2KHGVD44KT4MQJXGCTVSQKMBO6TVCY72F26TLXGWRL6PHGF6RNQC
OBFPJS2GHO2PEHBHGEHKIUOUAFIQHPIZXEVD2YIE3ZIE2PVMH5VAC
ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC
IZEVQF627FA7VV25KJAWYWGGC35LZUUBBQRPN5ZAINTQLPEDRTEAC
TNR3TEHKVADAEZSTOD2XLSUTSW5AWST2YUW4CWK5KE7DSC6XHZNAC
5W5M56VJFJEBXMGBVKGCKPHOEMVTKUOQMLPJP7VNDQLTYNJXXLHQC
Z7KS5XHHC6PAMTVBHXY7KUSS3BWAOU6FSYIITUCFOOJZU4OUJHBAC
NVOCQVASZWTKQJG7GPH7KHKZZR7NUG4WLV5YY4KAIRPCJRWCZPIAC
64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC
WO2MINIF4TXOHWSE7JWXRZYN64XRVLYIRFMF4SMPSOXKA2V77KMQC
HE3JTXO37O4MOMWPZ4BRBHP53KBPZDG3PCSUCVNOKIS7IY26OCIAC
4IQVQL4TS35GL2GYZJG254TKJLL5EHMRSFT77Z4VTRZIG2TMBM3QC
AXKKXBWN4EMUOLV43WN52JSKJPBV7TLSGLNJW5EZXHSJNKCYUWOQC
BROSTG5KP3NUNLSYPVQID254TE47E5RKQAKLPIY7PGWETE6JNMTAC
VJPT6HDRMJAJD5PT3VOYJYW43ISKLICEHLSDWSROX2XZWO2OFZPQC
BXGLKYRXO2O4NRM3BLNWQ7AWVPQXAMFS57MFYHJNOZZEZZW5BH6AC
local
stable
snaplets
regtest.conf
site_key.txt
module Quixotic.Util.Http where
import ClassyPrelude
import Data.ByteString (split)
import Data.Attoparsec.ByteString
import qualified Data.ByteString.Base64 as B64
type AuthHeader = (Text, ByteString)
authHeaderParser :: Parser AuthHeader
authHeaderParser = do
let isBase64Char w = (w >= 47 && w <= 57 ) ||
(w >= 64 && w <= 90 ) ||
(w >= 97 && w <= 122) ||
(w == 43 || w == 61 )
b64 <- string "Basic " *> takeWhile1 isBase64Char
decoded <- either fail pure $ B64.decode b64
case split 58 decoded of
(uname : pwd : []) -> pure $ (decodeUtf8 uname, pwd)
_ -> fail "Could not unpack auth header into username and password components"
#!/bin/bash
curl -v -u "nuttycom:kjntest" -H "Content-Type: application/json" -d '{"projectName":"the"}' http://localhost:8000/projects
#!/bin/bash
curl -v -H "Content-Type: application/json" -d '{"username":"nuttycom", "password":"kjntest", "email":"kris@quixoticcompany.com", "btcAddr":"1KamUn1BaRMd2HwikyQWGTdUvfPScg9QA5"}' http://localhost:8000/register
authHeaderParser :: Parser AuthHeader
authHeaderParser = do
let isBase64Char w = (w >= 47 && w <= 57 ) ||
(w >= 64 && w <= 90 ) ||
(w >= 97 && w <= 122) ||
(w == 43 || w == 61 )
b64 <- string "Basic" *> takeWhile1 isBase64Char
decoded <- either fail pure $ B64.decode b64
case split 58 decoded of
(uname : pwd : []) -> pure $ (decodeUtf8 uname, pwd)
_ -> fail "Could not unpack auth header into username and password components"
Just pid -> error $ "FIXME: implement project access check - got pid " ++ " " ++ show uid ++ " " ++ show pid
Just pid -> do
projects <- liftPG . runReaderT $ findUserProjects uid
if any (\p -> p ^. projectId == pid) projects
then pure pid
else snapError 403 $ "User " ++ (tshow uid) ++ " does not have access to project " ++ (tshow pid)
{-# LANGUAGE TemplateHaskell #-}
module Quixotic.Snaplet.Projects
( projectsHandler
) where
import ClassyPrelude
import Control.Lens
import Control.Monad.State
import Data.Aeson as A
import Quixotic
import Quixotic.Database
import Quixotic.Snaplet
import Quixotic.Snaplet.Auth
import Snap.Core
import Snap.Snaplet
import Snap.Snaplet.PostgresqlSimple
data CreateProject = CreateProject { createProjectName :: Text }
instance FromJSON CreateProject where
parseJSON (Object v) = CreateProject <$> v .: "projectName"
parseJSON _ = mzero
projectsHandler :: Handler App App ()
projectsHandler = do
void $ method POST projectCreateHandler
void $ method GET projectListHandler
projectCreateHandler :: Handler App App ProjectId
projectCreateHandler = do
QDB{..} <- view qdb <$> with qm get
uid <- requireUserId
requestBody <- readRequestBody 4096
cp <- maybe (snapError 400 "Could not parse project data") pure $ A.decode requestBody
timestamp <- liftIO getCurrentTime
liftPG . runReaderT . createProject $ Project (createProjectName cp) timestamp uid
projectListHandler :: Handler App App [Project]
projectListHandler = ok
authUser <- with auth $
AU.createUser (userData ^. (cuser.username._UserName)) (userData ^. password)
let createQUser = liftPG $ runReaderT (createUser $ userData ^. cuser)
let createSUser = AU.createUser (userData ^. (cuser.username._UserName)) (userData ^. password)
createQUser = liftPG $ runReaderT (createUser $ userData ^. cuser)
authUser <- with auth createSUser
btc_addr varchar(34) primary key
id serial primary key,
handle text not null,
btc_addr text not null,
email text not null
);
create table projects (
id serial primary key,
project_name text not null,
inception_date timestamp without time zone not null,
initiator_id integer references users (id) not null
btc_addr varchar(34) references users (btc_addr) not null,
trust_interval interval not null
)
project_id integer references projects(id) not null,
user_id integer references users(id) not null,
btc_addr text not null,
event_type event_t not null,
event_time timestamp without time zone not null
);
create type event_type as enum ('start_work', 'stop_work');
create table auctions (
id serial primary key,
project_id integer references projects(id) not null,
initiator_id integer references users (id) not null,
raise_amount numeric not null,
end_time timestamp without time zone not null
);
btc_addr varchar(34) references users (btc_addr) not null,
log_time timestamp without time zone not null,
log_type event_type not null
)
auction_id integer references projects (id) not null,
bidder_id integer references users (id) not null,
bid_seconds integer not null,
bid_amount numeric not null,
bid_time timestamp without time zone not null
);
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, NoImplicitPrelude #-}
module Quixotic.Util.HttpSpec where
import ClassyPrelude
import Quixotic.Util.Http
import Data.Attoparsec.ByteString
import Test.Hspec
spec :: Spec
spec = do
describe "HTTP Basic header parsing" $ do
it "parses the Basic auth header" $ do
let rawHeader = "Basic bnV0dHljb206a2pudGVzdA=="
(parseOnly authHeaderParser rawHeader) `shouldBe` (Right ("nuttycom", "kjntest"))
main :: IO ()
main = hspec spec