Authentication with Snap: Using a snaplet several times - haskell-snap-framework

I'm building a web application with Snap that needs to authenticate both staff and customers. So far I'm using the auth snaplet provided by snaplet-postgresql-simple to authenticate the two types of users from the same table in the database.
The initialization code therefore looks something like this:
s <- nestSnaplet "sess" sess $ initCookieSessionManager sessionKeyfile "sess" Nothing (Just sessionTimeout)
db <- nestSnaplet "pg" pg Pg.pgsInit
a <- nestSnaplet "auth" auth $ initPostgresAuth sess db
I'm considering separating the two types of users into two tables for these reasons:
the information associated with each type of user (i.e. the columns) is actually different (e.g. I don't need to know first and last names of staff)
I want to allow the staff to be authenticate to the backend without being logged into the frontend (I'd need separate cookies then, I guess)
I think security could benefit if the two types of users are in separate tables
I'm considering using two instances of the snaplets for postgresql-simple and sessions.
The initialization code would then look something like this:
s1 <- nestSnaplet "sess1" sess1 $ initCookieSessionManager sessionKeyfile "sess1" Nothing (Just sessionTimeout)
s2 <- nestSnaplet "sess2" sess2 $ initCookieSessionManager sessionKeyfile "sess2" Nothing (Just sessionTimeout)
db <- nestSnaplet "pg" pg Pg.pgsInit
a1 <- nestSnaplet "auth1" auth1 $ initPostgresAuth sess1 db
a2 <- nestSnaplet "auth2" auth2 $ initPostgresAuth sess2 db
Is that possible to use several instances of a snaplet like that?
Or does my problem have a better solution?

I wouldn't use two instances. I'd use a single instance where a user represents whatever is common to both, and then you add a user type column and put the extra information in other tables linked with a foreign key.

Related

Yesod return whole Entity after insert?

Basically right now I run fbId <- runDB $ insert myNewFooBar and I get back a Key FooBar. Is there any way to return the value of the whole FooBar directly from an insert without running a separate query of runDB $ get404 fbId after?
I just build the Entity Haskell-side: Entity fbId myNewFooBar.
Another, shorter option is to use insertEntity, which returns an entity instead of a record. Under the hood this function calls insert and builds an entity from the supplied record and the returned key (no additional DB queries).
insertedFooBar <- runDB $ insertEntity myNewFooBar

Terraform cyclic dependency challenge

Ok, so most of this is working except...
We have a user data template file for getting each new AWS server to register with Chef Automate. Chef refers to each client by the "node_name" set in the user data script, which is the instance id by default. But when viewing in the Chef UI or "knife node list", the instance id isn't exactly user friendly. We were able to write out a meaningful node_name using the template. Something like:
data "template_file" "user-data-qa" {
count = "${var.QA_numhosts}"
template = "${file("userdata.tpl")}"
vars {
node_name = "${var.customer_name}-QA-${format("%d", count.index + 1)}"
}
}
However, If we rebuild the instance, we get an error from Chef since the new instance tries to register with the same name, but a newly generated key.
So, we added a random number suffix to the node_name. We'd like this random number to be updated every time the instance is rebuilt. The first attempt was to set the instance id as the "keeper" for the random number. This resulted in the cycle error: ( -> means "depends on") Instance -> User Data -> Random -> Instance
Also tried dumping random generation and just appending a substring of the instance id to the node_name. Same problem, although a shorter cycle: Instance -> User Data -> Instance.
Any ideas for getting around this? In short, we want to append a string to the node_name, which gets inserted into the user data, and that string should update every time the instance is terminated and re-launched. Without cycle errors from Terraform.
instance-id, as you have seen, is calculated, so you can't use it in the definition of the resource itself, or through dependency cycles.
Rather than populate the instance ID inside Terraform, try having the user data script itself get the instance ID and populate node_name.rb with that value. You can do this with a simple curl and echo:
echo "node_name '$(curl --silent /dev/null http://169.254.169.254/latest/meta-data/instance-id)'" > path/to/node_name.rb

How can I include post teasers in my installation?

I'm following the official instructions on the Hakyll site to get post teasers up and running on my site. Unfortunately, I've hit a snag, and the instructions aren't much help.
I'm getting an out-of-scope error for the item value referenced in this snippet:
loadAndApplyTemplate
"template/postitem.html"
(teaserField "teaser" "content" <> defaultContext)
item
When embedding it into my site.hs. On a side note for reproducibility's sake, it also wasn't made clear where the <> operator came from; this required the import of one of Literate Haskell's modules.
It's completely unclear where this reference to item came from, and because it's a fairly common word, I have to sift through thousands of results even when using find and grep on my machine.
What should I declare or import to gain access to item here?
The tutorial page isn't a complete example. item isn't a reference to some function. It's just a placeholder name for an Item. Usually you'll get it from pandocCompiler or one of the many other "compilers". In this example, loadAndApplyTemplate is just like any other use of it. The only difference is that $teaser$ will be bound to the teaser text in the template.
That said, this isn't that great of an example since you usually want to use the teaser text on page listing multiple posts. This will probably involve using listField to make a collection of posts that you will iterate upon in the template. For example, this is the rule for my index page:
match "index.html" $ do
route idRoute
compile $ do
posts <- fmap (take indexRecentPostCount) . recentFirst =<< loadAllSnapshots postsPattern "postContent"
let indexCtx =
constField "title" "Home" <>
baseCtx
getResourceBody
>>= applyAsTemplate (listField "posts" (teaserField "teaser" "postContent" <> postCtx) (return posts) <> indexCtx)
>>= loadAndApplyDefaultTemplate indexCtx
>>= relativizeUrls
The "item" in this case is what getResourceBody returns, i.e. the body of index.html. This binds $posts$ to a list of posts. Ignoring the metadata, my index.html is just:
$for(posts)$
$partial("templates/teaser.html")$
$endfor$
$teaser$ is then bound in the template/teaser.html template.

Exception from Warp: stack overflow when logging in

I'm making a little app for work to handle shared to-do lists, I'm almost done but I'd like to add some very simple authentication. I followed the doc to add hashdb to the scaffolded site (https://github.com/yesodweb/yesod-cookbook/blob/master/cookbook/Using-HashDB-In-a-Scaffolded-Site.md), and it compiles fine, but when I log in with correct username / password (added by hand in the database) I get this :
10/Dec/2016:13:36:02 +0100 [Debug#SQL] SELECT `name`,`password` FROM `user` WHERE `name`=?; [PersistText "Ulrar"]
10/Dec/2016:13:36:08 +0100 [Error#yesod] Exception from Warp: stack overflow #(cstod_GjWCdZJB9K0EGPCbjz5gnP:Application Application.hs:133:15)
Line 133 of Application.hs is this one : $(qLocation >>= liftLoc)
That's from the default code.
As you can see my "User" table is pretty simple, I have a primary key on the name and a password, and that's it. The name must be unique, of course.
I'll be adding the few user by hand in the database, that'll be more than enough for us.
I tried the query by hand, it returns what you'd expect, and trying to log in with the wrong username / password does "work", it redirects to the login form with an error. Only using the correct username / password couple will give that Exception, and it does seem to load for a while before throwing it after clicking on the button.
I end up on http://localhost:3000/auth/page/hashdb/login with just "Something went wrong" written.
I assume I must have missed something, I'm using the yesod-mysql scaffolded site and I have this in the YesodAuth instance :
authPlugins app = [authHashDB (Just . UniqueUser)]
I removed the getAuthId definition since I had no idea what to put there, the definition from the doc doesn't compile because getAuthIdHashDB isn't exported anymore apparently. Is this my problem ?
Thanks !
Yep, my problem was indeed removing getAuthId !
Solved it by adding this instead :
authenticate creds = runDB $ do
x <- getBy $ UniqueUser $ credsIdent creds
case x of
Just (Entity uid _) -> return $ Authenticated uid
Nothing -> return $ UserError InvalidUsernamePass

Bind list value in ArangoDB query

I'm facing some issues when trying to bind a list variable in an ArangoDB query. More concretely, the list might look like as follows and comes from a URL parameter in a certain Foxx controller endpoint:
.../someAPIEndpoint?list=A,B,C,D
I would like to be able to do something like this:
stmt = db._createStatement({query: "for i in [#list] return i"});
stmt.bind('list', req.params('list').split(','));
Since I do not know how many values will I receive from the API call, I can't create n bindings for each possible one. Is what I want to achieve even possible?
Thanks in advance.
you were almost there, you can bind an array directly to the parameter (i just removed "[" and "]" from your query:
stmt = db._createStatement({query: "for i in #list return i"});
stmt.bind('list', req.params('list').split(','));

Resources