Enable postgres configuration via environment variable for Heroku.
[?]
May 28, 2015, 11:38 PM
V2VDN77HCSRYYWXDJJ2XOVHV4P6PVWNJZLXZ7JUYPQEZQIH5BZ3QCDependencies
- [2]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [3]
45QJYWN3Fixing up the README. Still struggling with the ending. - [4]
2Y2QZFVFSwitch to more modern cabal2nix-based workflow. - [5]
ADMKQQGCInitial empty Snap project. - [6]
NJZ3DKZYTHEY CAN TALK! - [7]
TNR3TEHKSwitch to Postgres + snaplet arch compiles. - [8]
Z3M53KTLAdrift. - [9]
7XN3I3QJAdd 'loggedIntervals' endpoint. - [10]
RSEB2NFGReplacing Snap with Scotty. - [11]
BROSTG5KBeginning of modularization of server. - [12]
VJPT6HDRFix remaining type errors after addition of login handler. - [13]
EYGIUUQZRestore remainder of endpoints to compiling status. - [14]
I2KHGVD4Require project permissions for access to most data. - [15]
A6HKMINBAttempting to improve JSON handling. - [16]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [17]
XTBSG4C7Adding serveJSON combinator to eliminate some boilerplate from handlers. - [18]
RPAJLHMTChange to use UUIDs instead of ints for primary keys. - [19]
W35DDBFYFactor common JSON conversions up into client lib module. - [20]
64C6AWH6Rename Ananke -> Quixotic, project reboot. - [21]
EMVTF2IWWIP moving back to snap. - [22]
LAROLAYUWIP - [23]
64VI73NPServer now compiles using abstracted SQLite - [24]
WZUHEZSBStart of migration back toward snap. - [25]
TCOAKCGGCompleted conversion to snap. - [26]
IZEVQF62Work in progress replacing sqlite with postgres. - [27]
O5FVTOM6Undo JSON silliness, enable a couple more routes.
Change contents
- edit in notes.md at line 23[3.19096]
(12:36:54 PM) nuttycom1: Reiterating my earlier question, this time via StackOverflow: http://stackoverflow.com/questions/30466275/http-basic-auth-in-snap(12:41:03 PM) dmj`: nuttycom1: why not use snap's cookie-based auth?(12:41:33 PM) carter_cloud: mightybyte: lpsmith did that posgres-simple snaplet transaction bug ever get fixed?(12:41:40 PM) nuttycom1: dmj`: my clients aren't necessarily browsers.(12:41:55 PM) nuttycom1: In fact, they're mostly not browsers.(12:43:17 PM) dmj`: nuttycom1: are they mobile clients?(12:43:31 PM) nuttycom1: dmj`: no, other servers.(12:48:21 PM) dmj`: nuttycom1: basic auth isn't secure, you're sending the password (base-64 encoded) on every request, it could be fine if it's all over https. But still, a token based system would be more secure. Are these severs internal / external? (i.e. yours or someone elses)(12:53:16 PM) nuttycom1: dmj`: Token-based systems are no more secure if they're not over https, but that's a side point; in my experience it's common in any case to use the Authorization headers to carry authentication tokens so that they don't have to pollute the request payload in other ways.(12:54:14 PM) nuttycom1: I have a bunch of CLI tools that access my services via curl and such, which prompt for auth credentials along the way. Using Basic is convenient for these kinds of tools.(12:54:49 PM) nuttycom1: All the servers *are* in my control, however. I suppose I could use an X-My-Auth header or some such, but I don't much see the point.(12:55:38 PM) nuttycom1: At least, they're under my control at the moment, but obviously the API I'm providing is ultimately going to be accessible to anyone on the open web, so I might as well make things easy for them.(12:59:00 PM) mightybyte: carter_cloud: Yes(12:59:32 PM) mightybyte: http://blog.melding-monads.com/2015/02/12/announcing-snaplet-postgresql-simple-0-6/(01:00:42 PM) dmj`: nuttycom1: token based systems only transmit the credentials on the initialize request to generate the token, not on every request, so not sure how it's just as secure. It seems like setting up your own oauth provider would be ideal in this case, especially if you plan on releasing this api to the public. If your servers are not exposed to the outside world, why even have them authenticate against each other, if you're using A(01:00:43 PM) dmj`: you using a VPC?(01:00:49 PM) dmj`: initial*(01:00:52 PM) mightybyte: carter_cloud: I guess that post never hit reddit.(01:02:17 PM) mightybyte: nuttycom1: I don't know of a basic auth package for snap (probably in part due to the concerns dmj` has pointed out), but I wouldn't expect that it would be too hard to write.(01:02:42 PM) nuttycom1: mightybyte: Hah.... yeah, I provide one as context in my SO question.(01:04:49 PM) carter_cloud: mightybyte: btw Stephen Diehl has a neat prototype of a streaming Postgres binding https://github.com/elsen-trading/pgstream(01:07:49 PM) nuttycom1: dmj`: fair enough; I suppose that I can add token handling to the CLI tools, it's just a little more work since I'll have to encrypt the token before it's stored locally, and require the user to decrypt on each request rather than provide credentials. That's probably the right way to go.(01:07:51 PM) dmj`: nuttycom1: I can't speak for other token-based systems, but json web tokens are typically encrpyted (HMAC SHA-256), basic auth data is just encoded. So your last line of defense in basic auth is ssl, and your surface area (every request) is much larger. I dunno, if you think basic auth suits your needs then go for it, but it sounds to me like you should become an oauth provider here.(01:08:16 PM) mightybyte: nuttycom1: responded(01:09:10 PM) nuttycom1: mightybyte: thanks!(01:10:45 PM) dmj`: nuttycom1: yea, you can create your own key server! Then create tcp connections from your key server to all other api servers. You should change the key somewhat frequently (This secret key is used to hash all tokens). So your key server can broadcast the new secret to all servers via tcp, then (if you're using haskell and not that node.js single-threaded stuff) you could migrate everyones tokens over to the new key transparen(01:10:45 PM) dmj`: if new key has been sent, then rehash with it, and put it in the header).(01:11:16 PM) dmj`: only one-thread, I don't know how people do it(01:11:20 PM) dmj`: one thread*(01:12:18 PM) nuttycom1: Interesting, thanks dmj`. Looks like I have some additional investigation to do. - file addition: QConfig.hs[2.2063]
module Aftok.QConfig whereimport ClassyPreludeimport qualified Data.ByteString.Char8 as Cimport qualified Data.Configurator as Cimport qualified Data.Configurator.Types as CTimport System.Environmentimport System.IO(FilePath)import Snap.Coreimport Snap.Snaplet.PostgresqlSimpleimport qualified Snap.Http.Server.Config as SCdata QConfig = QConfig{ hostname :: ByteString, port :: Int, authSiteKey :: System.IO.FilePath, cookieTimeout :: Maybe Int, pgsConfig :: PGSConfig-- , sslCert :: FilePath-- , sslKey :: FilePath-- , dbName :: String}loadQConfig :: System.IO.FilePath -> IO QConfigloadQConfig cfgFile = doenv <- getEnvironmentcfg <- C.load [C.Required cfgFile]let dbEnvCfg = pgsDefaultConfig . C.pack <$> lookup "DATABASE_URL" envreadQConfig cfg dbEnvCfgreadQConfig :: CT.Config -> Maybe PGSConfig -> IO QConfigreadQConfig cfg pc =QConfig <$> C.lookupDefault "localhost" cfg "hostname"<*> C.lookupDefault 8000 cfg "port"<*> C.require cfg "siteKey"<*> C.lookup cfg "cookieTimeout"<*> maybe (mkPGSConfig $ C.subconfig "db" cfg) pure pc-- <*> (fmap fpFromText $ C.require cfg "sslCert")-- <*> (fmap fpFromText $ C.require cfg "sslKey")-- <*> C.require cfg "db"baseSnapConfig :: MonadSnap m => QConfig -> SC.Config m a -> SC.Config m abaseSnapConfig qc =SC.setHostname (hostname qc) .SC.setPort (port qc)--SC.setSSLPort (port qc) .--SC.setSSLCert (fpToString $ sslCert qc) .--SC.setSSLKey (fpToString $ sslKey cfg)-- configuration specific to Snap, commandLineConfig arguments override-- config file.snapConfig :: QConfig -> IO (SC.Config Snap a)snapConfig qc = SC.commandLineConfig $ baseSnapConfig qc SC.emptyConfig - edit in server/Main.hs at line 1[4.1040]→[4.1040:1109](∅→∅),[4.89]→[4.5255:5256](∅→∅),[4.128]→[4.5255:5256](∅→∅),[4.1109]→[4.5255:5256](∅→∅),[4.1314]→[4.5255:5256](∅→∅),[4.1459]→[4.5255:5256](∅→∅),[4.7548]→[4.5255:5256](∅→∅),[4.5255]→[4.5255:5256](∅→∅)
{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE TemplateHaskell #-} - edit in server/Main.hs at line 5[4.35]→[4.112:152](∅→∅),[4.1165]→[4.112:152](∅→∅),[4.2439]→[4.112:152](∅→∅),[4.152]→[4.123:170](∅→∅)
import qualified Data.Configurator as Cimport qualified Data.Configurator.Types as CT - edit in server/Main.hs at line 6
import System.IO(FilePath) - edit in server/Main.hs at line 10
import Aftok.QConfig - edit in server/Main.hs at line 18
import Snap.Http.Serverimport qualified Snap.Http.Server.Config as SC - edit in server/Main.hs at line 22[4.218]→[4.5492:5493](∅→∅),[4.1477]→[4.5492:5493](∅→∅),[4.1633]→[4.5492:5493](∅→∅),[4.2593]→[4.5492:5493](∅→∅),[4.7874]→[4.5492:5493](∅→∅),[4.5492]→[4.5492:5493](∅→∅),[4.1932]→[4.1932:1955](∅→∅),[4.1955]→[4.340:383](∅→∅),[4.383]→[4.7748:7786](∅→∅),[4.7786]→[4.7903:7934](∅→∅),[4.7903]→[4.7903:7934](∅→∅),[4.7934]→[4.42:95](∅→∅),[4.383]→[4.42:95](∅→∅),[4.95]→[4.7935:7959](∅→∅),[4.7959]→[4.309:314](∅→∅),[4.309]→[4.309:314](∅→∅)
data QConfig = QConfig{ hostname :: ByteString, port :: Int, authSiteKey :: System.IO.FilePath, cookieTimeout :: Maybe Int-- , sslCert :: FilePath-- , sslKey :: FilePath-- , dbName :: String} - edit in server/Main.hs at line 27
--simpleHttpServe sconf $ runReaderT (site sqliteQDB) db - replacement in server/Main.hs at line 34
pgs <- nestSnaplet "db" db pgsInitpgs <- nestSnaplet "db" db $ pgsInit' pgsConfig - edit in server/Main.hs at line 64[4.252]→[4.9050:9051](∅→∅),[4.9050]→[4.9050:9051](∅→∅),[4.9051]→[4.8019:8067](∅→∅),[4.8067]→[4.469:495](∅→∅),[4.469]→[4.469:495](∅→∅),[4.495]→[4.3939:3976](∅→∅),[4.3976]→[4.496:633](∅→∅),[4.555]→[4.496:633](∅→∅),[4.633]→[4.96:143](∅→∅),[4.143]→[4.3977:4016](∅→∅),[4.4016]→[4.9110:9154](∅→∅),[4.9110]→[4.9110:9154](∅→∅),[4.9154]→[4.143:264](∅→∅),[4.143]→[4.143:264](∅→∅),[4.264]→[4.9155:9192](∅→∅),[4.9192]→[4.829:962](∅→∅),[4.829]→[4.829:962](∅→∅),[4.962]→[4.265:411](∅→∅)
loadQConfig :: System.IO.FilePath -> IO QConfigloadQConfig cfgFile = docfg <- C.load [C.Required cfgFile]parseQConfig cfgparseQConfig :: CT.Config -> IO QConfigparseQConfig cfg =QConfig <$> C.lookupDefault "localhost" cfg "hostname"<*> C.lookupDefault 8000 cfg "port"<*> C.require cfg "siteKey"<*> C.lookup cfg "cookieTimeout"-- <*> (fmap fpFromText $ C.require cfg "sslCert")-- <*> (fmap fpFromText $ C.require cfg "sslKey")-- <*> C.require cfg "db"baseSnapConfig :: MonadSnap m => QConfig -> SC.Config m a -> SC.Config m abaseSnapConfig cfg =SC.setHostname (hostname cfg) .SC.setPort (port cfg)--SC.setSSLPort (port cfg) .--SC.setSSLCert (fpToString $ sslCert cfg) .--SC.setSSLKey (fpToString $ sslKey cfg) - edit in server/Main.hs at line 65[4.1078]→[4.9193:9240](∅→∅),[4.9240]→[4.1126:1197](∅→∅),[4.1126]→[4.1126:1197](∅→∅),[4.1197]→[4.1121:1122](∅→∅)
snapConfig :: QConfig -> IO (SC.Config Snap a)snapConfig cfg = SC.commandLineConfig $ baseSnapConfig cfg emptyConfig