Re: [Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-07 Thread Jason Harrer
For those who may not be watching our Github repo, a patch was submitted
yesterday on Github to fix the redirect problem.  I downloaded the patch to
the Discourse SSO branch I've been working with, and Sidney is now able to
login properly the first time she clicks on "Log In" from Discourse:  She
gets redirected to the Snowdrift.coop login, enters her credentials, clicks
"Log in", and *ultimately* gets redirected back to Discourse logged in
(technically, a bit more in depth than that, but that's ok).

There were concerns presented that this broke things previously, which is
why it was changed.  Based on what we have now, it appears to be working.
I'll test a little bit more manually for now, but ultimately, we need some
automated tests written to make sure this works.  I have not learned how to
write unit tests, so if someone else is up for the challenge OR if someone
wants to spend time showing me how to do so, I would definitely appreciate
the help.

If someone can get me a patch for the unit tests, I can bundle up all the
various patches into one MR back to git.snowdrift.coop and help Salt get
this up and running.

Thanks!!

- Jason

On Tue, Dec 6, 2016 at 2:11 PM, Stephen Michel 
wrote:

> On Tue, Dec 6, 2016 at 12:30 PM, fr33domlover 
> wrote:
>
>> So, Bryan, waiting for your input on this. For now, I suppose it's not
>> super
>> critical, but it definitely will annoy and confuse users once the
>> Discourse
>> instance starts getting filled with people and messages (or is it
>> alreday? I
>> didn't check).
>>
>> -- fr33
>>
>
> When last I heard, the plan was to completely wipe the current Discourse
> instance and start clean with SSO. To that end, I believe the only people
> with accounts on Discourse are team members who have been manually invited.
>
> I'm not the most in the loop, so take this a grain of salt.
>
> ~Stephen
>
>
> ___
> Dev mailing list
> Dev@lists.snowdrift.coop
> https://lists.snowdrift.coop/mailman/listinfo/dev
>
___
Dev mailing list
Dev@lists.snowdrift.coop
https://lists.snowdrift.coop/mailman/listinfo/dev


Re: [Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-06 Thread fr33domlover
Hello,

Jason, thanks for the testing and the report!


On Tue, 6 Dec 2016 07:37:04 -0600
Jason Harrer  wrote:

> [...]
> 
> George is already signed up *and* signed in to Snowdrift.coop and goes to
> Discourse for the first time.  George will still see a button at the top
> that says "Log in".  In order for George to post as George, he still needs
> to click the "Log in" button in Discourse.  When he clicks the "Log in"
> button, the SSO logic will happen and George will then be logged in without
> having to enter any additional username or password information.  (yay!)
>  At this point, I'm looking into trying to figure out if there's a way for
> George to simply be logged in to Discourse without ever having to click
> "Log in".

That would be Discourse's responsibility. Basically, it would be possible if
once you load the Discourse instance website in your browser, either it
automatically redirects to SSO login (but that's bad because some users just
want to read the conversation, not log in), or a piece of Javascript somehow
sends a request to Snowdrift asynchronously. Not sure if it can happen without
custom modifications to Snowdrift. I don't know much JS, someone with JS
experience could perhaps know more.

> Sidney is already signed up for Snowdrift.coop, but she is not currently
> logged in to Snowdrift.coop.  Sidney goes to Discourse and sees the same
> screen as George did, with the "Log in" button at the top.  Sidney clicks
> on "Log in", and the sso logic redirects her to the Snowdrift.coop login
> page.  She enters her username and password, and clicks the "Log in" button
> on the Snowdrift.coop auth page, and she is redirected to...  her
> Dashboard.   When Sidney manually goes back to Discourse, she still has a
> "Log in" button on the Discourse page.  When she clicks "Log in" *this* time,
> the sso logic works.
> 
> The Snowdrift login page should have been redirected back to the sso
> handler, which would have then redirect back to Discourse with the
> authentication information (assuming we can keep the GET strings
> transferred throughout the various redirects).  I tried looking at Bryan's
> auth code, but apparently my Haskell skillset is not advanced enough to
> fully follow along.  I'm not sure what to do to make the modification.  If
> anyone can help with this, I would greatly appreciate it.

That's indeed an issue with Bryan's auth code. Yesod has a "final destination"
feature which lets you send the user a web page while specifying where to
redirect after that, and that sent page's handler can use the info to actually
redirect. That feature originally exists for the auth system: If you load a
page that requires login, you're redirected to login with the "final
destination" being the page from which you came. That way, after login you are
redirected back to where you were.

Bryan's code, which replaces YesodAuth, doesn't have that part implemented, or
it's still buggy.

So, Bryan, waiting for your input on this. For now, I suppose it's not super
critical, but it definitely will annoy and confuse users once the Discourse
instance starts getting filled with people and messages (or is it alreday? I
didn't check).

-- fr33

> Thanks!!
> 
> - Jason
> 
> On Mon, Dec 5, 2016 at 4:11 PM,  wrote:
> 
> > From: fr33domlover 
> >
> > ---
> >  website/Snowdrift.cabal  |  8 
> >  website/config/routes|  2 +
> >  website/config/settings.yml  |  3 ++
> >  website/src/Application.hs   |  1 +
> >  website/src/Discourse.hs | 86 ++
> > ++
> >  website/src/Handler/Discourse.hs | 49 +++
> >  website/src/Settings.hs  |  2 +
> >  7 files changed, 151 insertions(+)
> >  create mode 100644 website/src/Discourse.hs
> >  create mode 100644 website/src/Handler/Discourse.hs
> >
> > diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
> > index 9c1664c..8eca05d 100644
> > --- a/website/Snowdrift.cabal
> > +++ b/website/Snowdrift.cabal
> > @@ -40,6 +40,7 @@ library
> >  Avatar
> >  Application
> >  Css
> > +Discourse
> >  Email
> >  Foundation
> >  Import
> > @@ -49,6 +50,7 @@ library
> >  Settings.StaticFiles
> >  Handler
> >  Handler.Dashboard
> > +Handler.Discourse
> >  Handler.PaymentInfo
> >  Handler.Pledge
> >  Handler.Project
> > @@ -70,10 +72,12 @@ library
> >  , crowdmatch
> >  -- Other
> >  , aeson  >= 0.6 && < 0.12
> > +, base64-bytestring
> >  , blaze-html
> >  , bytestring >= 0.9 && < 0.11
> >  , classy-prelude >= 0.10.2
> >  , classy-prelude-yesod   >= 0.10.2
> > +, cryptonite
> >  , data-default
> >  , errors
> >  , esqueleto
> > @@ -82,8 +86,10 @@ library
> >

Re: [Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-06 Thread Jason Harrer
I tested this locally (I set up a local instance of Discourse to test
with), and it works...  to a point.  It seemed easier yesterday to explain
things in User Story format, so I'm going to attempt the same here.


George is already signed up *and* signed in to Snowdrift.coop and goes to
Discourse for the first time.  George will still see a button at the top
that says "Log in".  In order for George to post as George, he still needs
to click the "Log in" button in Discourse.  When he clicks the "Log in"
button, the SSO logic will happen and George will then be logged in without
having to enter any additional username or password information.  (yay!)
 At this point, I'm looking into trying to figure out if there's a way for
George to simply be logged in to Discourse without ever having to click
"Log in".

Sidney is already signed up for Snowdrift.coop, but she is not currently
logged in to Snowdrift.coop.  Sidney goes to Discourse and sees the same
screen as George did, with the "Log in" button at the top.  Sidney clicks
on "Log in", and the sso logic redirects her to the Snowdrift.coop login
page.  She enters her username and password, and clicks the "Log in" button
on the Snowdrift.coop auth page, and she is redirected to...  her
Dashboard.   When Sidney manually goes back to Discourse, she still has a
"Log in" button on the Discourse page.  When she clicks "Log in" *this* time,
the sso logic works.

The Snowdrift login page should have been redirected back to the sso
handler, which would have then redirect back to Discourse with the
authentication information (assuming we can keep the GET strings
transferred throughout the various redirects).  I tried looking at Bryan's
auth code, but apparently my Haskell skillset is not advanced enough to
fully follow along.  I'm not sure what to do to make the modification.  If
anyone can help with this, I would greatly appreciate it.

Thanks!!

- Jason

On Mon, Dec 5, 2016 at 4:11 PM,  wrote:

> From: fr33domlover 
>
> ---
>  website/Snowdrift.cabal  |  8 
>  website/config/routes|  2 +
>  website/config/settings.yml  |  3 ++
>  website/src/Application.hs   |  1 +
>  website/src/Discourse.hs | 86 ++
> ++
>  website/src/Handler/Discourse.hs | 49 +++
>  website/src/Settings.hs  |  2 +
>  7 files changed, 151 insertions(+)
>  create mode 100644 website/src/Discourse.hs
>  create mode 100644 website/src/Handler/Discourse.hs
>
> diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
> index 9c1664c..8eca05d 100644
> --- a/website/Snowdrift.cabal
> +++ b/website/Snowdrift.cabal
> @@ -40,6 +40,7 @@ library
>  Avatar
>  Application
>  Css
> +Discourse
>  Email
>  Foundation
>  Import
> @@ -49,6 +50,7 @@ library
>  Settings.StaticFiles
>  Handler
>  Handler.Dashboard
> +Handler.Discourse
>  Handler.PaymentInfo
>  Handler.Pledge
>  Handler.Project
> @@ -70,10 +72,12 @@ library
>  , crowdmatch
>  -- Other
>  , aeson  >= 0.6 && < 0.12
> +, base64-bytestring
>  , blaze-html
>  , bytestring >= 0.9 && < 0.11
>  , classy-prelude >= 0.10.2
>  , classy-prelude-yesod   >= 0.10.2
> +, cryptonite
>  , data-default
>  , errors
>  , esqueleto
> @@ -82,8 +86,10 @@ library
>  , formattable
>  , hjsmin >= 0.1
>  , http-client
> +, http-types
>  , lens
>  , libravatar
> +, memory
>  , mime-mail
>  , monad-logger   >= 0.3 && < 0.4
>  , nonce
> @@ -99,6 +105,7 @@ library
>  , text   >= 0.11&& < 2.0
>  , time
>  , titlecase
> +, transformers
>  , unordered-containers
>  , wai
>  , wai-extra  >= 3.0 && < 3.1
> @@ -122,6 +129,7 @@ library
>  RecordWildCards
>  ScopedTypeVariables
>  TemplateHaskell
> +TupleSections
>  TypeFamilies
>  ViewPatterns
>
> diff --git a/website/config/routes b/website/config/routes
> index 9e4e079..30fdef8 100644
> --- a/website/config/routes
> +++ b/website/config/routes
> @@ -26,6 +26,8 @@
>  /p/snowdrift SnowdriftProjectR GET
>  /pledge/snowdrift PledgeSnowdriftR POST DELETE
>
> +/discourse/sso DiscourseR GET
> +
>  -- ## Backward compatibility routes
>
>  -- Prevents breakage of external links to the old wiki. See
> diff --git a/website/config/settings.yml b/website/config/settings.yml
> index 3c30c0f..61ff751 100644
> --- a/website/config/settings.yml
> +++ b/website/config/settings.yml
> @@ -44,3 +44,6 @@ send-email: "_env:SD_EMAILS:false"
>  # chreekat for assistance.
>  stripe-secret-key: 

Re: [Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-05 Thread fr33domlover
Hello Bryan,

If you look at the code you'll see there are 2 parts. The logic, which is a
simple single file, and the handler that uses it. Both are quite trivial, it's
hardly worth having them separately. I do think it would be great to have them
both as a separate package that offers a Yesod subsite or at least the handler
function.

I didn't do that because Snowdrift uses its own auth class so if I
used YesodAuth we wouldn't be able to use the library. Sure I can add some
AuthBackend class stuff in the library, but that just begins to be ugly and not
worth it, at least at the beginning. The quickest way to get the code ready was
to add it straight to the web app,

I already sent a new patch, JazzyEagle and I are debugging it and very soon the
code will be ready. After that, we can think about moving the code to a
separate package and solving issues like the auth class. I'm not sure *I* will
be doing that part, but hey, at least I wrote the SSO implementation itself :-)

--fr33



On Mon, 5 Dec 2016 11:34:17 -0800
Bryan Richter  wrote:

> Thanks for this. I think the best way to make use of this is to package
> up the logic specific to handling SSO requests in an independent
> library, so we can use it later.
> 
> That's not about you, or about this code. :) It's just that I am going
> to push back hard on having SSO be the second major feature we add to
> our website (or the third, or even the tenth).
> 
> We'll see where that discussion goes.
> 
> On Mon, Dec 05, 2016 at 04:13:42AM +0200, fr33domlover wrote:
> > From: fr33domlover 
> > 
> > ---
> >  website/Snowdrift.cabal  |  7 
> >  website/config/routes|  2 ++
> >  website/config/settings.yml  |  4 +++
> >  website/src/Application.hs   |  1 +
> >  website/src/Discourse.hs | 73
> >  website/src/Handler/Discourse.hs |
> > 55 ++ website/src/Settings.hs  |  4 +++
> >  7 files changed, 146 insertions(+)
> >  create mode 100644 website/src/Discourse.hs
> >  create mode 100644 website/src/Handler/Discourse.hs
> > 
> > diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
> > index 9c1664c..b5cfaa5 100644
> > --- a/website/Snowdrift.cabal
> > +++ b/website/Snowdrift.cabal
> > @@ -40,6 +40,7 @@ library
> >  Avatar
> >  Application
> >  Css
> > +Discourse
> >  Email
> >  Foundation
> >  Import
> > @@ -49,6 +50,7 @@ library
> >  Settings.StaticFiles
> >  Handler
> >  Handler.Dashboard
> > +Handler.Discourse
> >  Handler.PaymentInfo
> >  Handler.Pledge
> >  Handler.Project
> > @@ -74,6 +76,7 @@ library
> >  , bytestring >= 0.9 && < 0.11
> >  , classy-prelude >= 0.10.2
> >  , classy-prelude-yesod   >= 0.10.2
> > +, cryptonite
> >  , data-default
> >  , errors
> >  , esqueleto
> > @@ -82,8 +85,10 @@ library
> >  , formattable
> >  , hjsmin >= 0.1
> >  , http-client
> > +, http-types
> >  , lens
> >  , libravatar
> > +, memory
> >  , mime-mail
> >  , monad-logger   >= 0.3 && < 0.4
> >  , nonce
> > @@ -99,6 +104,7 @@ library
> >  , text   >= 0.11&& < 2.0
> >  , time
> >  , titlecase
> > +, transformers
> >  , unordered-containers
> >  , wai
> >  , wai-extra  >= 3.0 && < 3.1
> > @@ -122,6 +128,7 @@ library
> >  RecordWildCards
> >  ScopedTypeVariables
> >  TemplateHaskell
> > +TupleSections
> >  TypeFamilies
> >  ViewPatterns
> >  
> > diff --git a/website/config/routes b/website/config/routes
> > index 9e4e079..30fdef8 100644
> > --- a/website/config/routes
> > +++ b/website/config/routes
> > @@ -26,6 +26,8 @@
> >  /p/snowdrift SnowdriftProjectR GET
> >  /pledge/snowdrift PledgeSnowdriftR POST DELETE
> >  
> > +/discourse/sso DiscourseR GET
> > +
> >  -- ## Backward compatibility routes
> >  
> >  -- Prevents breakage of external links to the old wiki. See
> > diff --git a/website/config/settings.yml b/website/config/settings.yml
> > index 3c30c0f..e4e30bb 100644
> > --- a/website/config/settings.yml
> > +++ b/website/config/settings.yml
> > @@ -44,3 +44,7 @@ send-email: "_env:SD_EMAILS:false"
> >  # chreekat for assistance.
> >  stripe-secret-key: "_env:STRIPE_SECRET_KEY:"
> >  stripe-publishable-key: "_env:STRIPE_PUBLISHABLE_KEY:"
> > +
> > +# Discourse SSO
> > +discourse-url: "https://discourse.snowdrift.coop;
> > +discourse-sso-secret: ""
> > diff --git a/website/src/Application.hs b/website/src/Application.hs
> > index 8ccd6b2..761d3f8 100644
> > --- a/website/src/Application.hs
> > +++ b/website/src/Application.hs
> > @@ -35,6 +35,7 @@ import qualified 

[Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-05 Thread fr33domlover
From: fr33domlover 

---
 website/Snowdrift.cabal  |  8 
 website/config/routes|  2 +
 website/config/settings.yml  |  3 ++
 website/src/Application.hs   |  1 +
 website/src/Discourse.hs | 86 
 website/src/Handler/Discourse.hs | 49 +++
 website/src/Settings.hs  |  2 +
 7 files changed, 151 insertions(+)
 create mode 100644 website/src/Discourse.hs
 create mode 100644 website/src/Handler/Discourse.hs

diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
index 9c1664c..8eca05d 100644
--- a/website/Snowdrift.cabal
+++ b/website/Snowdrift.cabal
@@ -40,6 +40,7 @@ library
 Avatar
 Application
 Css
+Discourse
 Email
 Foundation
 Import
@@ -49,6 +50,7 @@ library
 Settings.StaticFiles
 Handler
 Handler.Dashboard
+Handler.Discourse
 Handler.PaymentInfo
 Handler.Pledge
 Handler.Project
@@ -70,10 +72,12 @@ library
 , crowdmatch
 -- Other
 , aeson  >= 0.6 && < 0.12
+, base64-bytestring
 , blaze-html
 , bytestring >= 0.9 && < 0.11
 , classy-prelude >= 0.10.2
 , classy-prelude-yesod   >= 0.10.2
+, cryptonite
 , data-default
 , errors
 , esqueleto
@@ -82,8 +86,10 @@ library
 , formattable
 , hjsmin >= 0.1
 , http-client
+, http-types
 , lens
 , libravatar
+, memory
 , mime-mail
 , monad-logger   >= 0.3 && < 0.4
 , nonce
@@ -99,6 +105,7 @@ library
 , text   >= 0.11&& < 2.0
 , time
 , titlecase
+, transformers
 , unordered-containers
 , wai
 , wai-extra  >= 3.0 && < 3.1
@@ -122,6 +129,7 @@ library
 RecordWildCards
 ScopedTypeVariables
 TemplateHaskell
+TupleSections
 TypeFamilies
 ViewPatterns
 
diff --git a/website/config/routes b/website/config/routes
index 9e4e079..30fdef8 100644
--- a/website/config/routes
+++ b/website/config/routes
@@ -26,6 +26,8 @@
 /p/snowdrift SnowdriftProjectR GET
 /pledge/snowdrift PledgeSnowdriftR POST DELETE
 
+/discourse/sso DiscourseR GET
+
 -- ## Backward compatibility routes
 
 -- Prevents breakage of external links to the old wiki. See
diff --git a/website/config/settings.yml b/website/config/settings.yml
index 3c30c0f..61ff751 100644
--- a/website/config/settings.yml
+++ b/website/config/settings.yml
@@ -44,3 +44,6 @@ send-email: "_env:SD_EMAILS:false"
 # chreekat for assistance.
 stripe-secret-key: "_env:STRIPE_SECRET_KEY:"
 stripe-publishable-key: "_env:STRIPE_PUBLISHABLE_KEY:"
+
+# Discourse SSO
+discourse-sso-secret: ""
diff --git a/website/src/Application.hs b/website/src/Application.hs
index 8ccd6b2..761d3f8 100644
--- a/website/src/Application.hs
+++ b/website/src/Application.hs
@@ -35,6 +35,7 @@ import qualified Yesod.GitRev as G
 
 import Handler
 import Handler.Dashboard
+import Handler.Discourse
 import Handler.PaymentInfo
 import Handler.Pledge
 import Handler.Project
diff --git a/website/src/Discourse.hs b/website/src/Discourse.hs
new file mode 100644
index 000..83106bb
--- /dev/null
+++ b/website/src/Discourse.hs
@@ -0,0 +1,86 @@
+module Discourse where
+
+import Prelude
+
+import Control.Monad.Trans.Except
+import Crypto.Hash.Algorithms (SHA256)
+import Crypto.MAC.HMAC
+import Data.ByteArray.Encoding
+import Data.ByteString (ByteString)
+import Data.Maybe (catMaybes)
+import Data.Text (Text, pack)
+import Data.Text.Encoding (encodeUtf8, decodeUtf8')
+import Network.HTTP.Types.URI (renderSimpleQuery, parseSimpleQuery)
+
+import qualified Data.ByteString as B (drop)
+import qualified Data.ByteString.Base64 as B64 (decodeLenient)
+
+import Model
+
+-- | Information we send back to Discourse once the user logs in through our
+-- UI.
+data UserInfo = UserInfo
+{ ssoEmail :: Text
+, ssoId:: UserId
+, ssoUsername  :: Maybe Text
+, ssoFullName  :: Maybe Text
+, ssoAvatarUrl :: Maybe Text
+, ssoBio   :: Maybe Text
+}
+
+-- | Type restricted convenience wrapper that computes our HMAC.
+hmacSHA256 :: ByteString -> ByteString -> HMAC SHA256
+hmacSHA256 = hmac
+
+-- | Given secret known in advance and payload given in the query, compute the
+-- HMAC-SHA256, to which Discourse refers as the signature.
+generateSig
+:: ByteString -- ^ Secret
+-> ByteString -- ^ Base64 encoded payload
+-> ByteString
+generateSig secret payload =
+convertToBase Base16 $ hmacGetDigest $ hmacSHA256 secret payload
+
+-- | This validates the payloads's authenticity (i.e. make sure it's really our
+-- trusted local Discourse instance) by using the signature as a proof that it
+-- knows the SSO secret. This is done 

Re: [Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-05 Thread Aaron Wolf
On 12/05/2016 11:34 AM, Bryan Richter wrote:
> Thanks for this. I think the best way to make use of this is to package
> up the logic specific to handling SSO requests in an independent
> library, so we can use it later.
> 
> That's not about you, or about this code. :) It's just that I am going
> to push back hard on having SSO be the second major feature we add to
> our website (or the third, or even the tenth).
> 
> We'll see where that discussion goes.
> 

I *strongly* like the idea of packaging SSO between Yesod app and
Discourse as an independent library we just use because that's in
general a great way to deal with generalized stuff like that.

But I want SSO implemented ASAP, immediately if possible. The
cleanliness and ease of managing everything about our use of Discourse
going forward and the site itself will be really nice if we start
Discourse with SSO from the get-go. I don't want all sorts of awkward
challenges in transition later after we have bunches of disconnected
users on each system and then try to push SSO then.

> On Mon, Dec 05, 2016 at 04:13:42AM +0200, fr33domlover wrote:
>> From: fr33domlover 
>>
>> ---
>>  website/Snowdrift.cabal  |  7 
>>  website/config/routes|  2 ++
>>  website/config/settings.yml  |  4 +++
>>  website/src/Application.hs   |  1 +
>>  website/src/Discourse.hs | 73 
>> 
>>  website/src/Handler/Discourse.hs | 55 ++
>>  website/src/Settings.hs  |  4 +++
>>  7 files changed, 146 insertions(+)
>>  create mode 100644 website/src/Discourse.hs
>>  create mode 100644 website/src/Handler/Discourse.hs
>>
>> diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
>> index 9c1664c..b5cfaa5 100644
>> --- a/website/Snowdrift.cabal
>> +++ b/website/Snowdrift.cabal
>> @@ -40,6 +40,7 @@ library
>>  Avatar
>>  Application
>>  Css
>> +Discourse
>>  Email
>>  Foundation
>>  Import
>> @@ -49,6 +50,7 @@ library
>>  Settings.StaticFiles
>>  Handler
>>  Handler.Dashboard
>> +Handler.Discourse
>>  Handler.PaymentInfo
>>  Handler.Pledge
>>  Handler.Project
>> @@ -74,6 +76,7 @@ library
>>  , bytestring >= 0.9 && < 0.11
>>  , classy-prelude >= 0.10.2
>>  , classy-prelude-yesod   >= 0.10.2
>> +, cryptonite
>>  , data-default
>>  , errors
>>  , esqueleto
>> @@ -82,8 +85,10 @@ library
>>  , formattable
>>  , hjsmin >= 0.1
>>  , http-client
>> +, http-types
>>  , lens
>>  , libravatar
>> +, memory
>>  , mime-mail
>>  , monad-logger   >= 0.3 && < 0.4
>>  , nonce
>> @@ -99,6 +104,7 @@ library
>>  , text   >= 0.11&& < 2.0
>>  , time
>>  , titlecase
>> +, transformers
>>  , unordered-containers
>>  , wai
>>  , wai-extra  >= 3.0 && < 3.1
>> @@ -122,6 +128,7 @@ library
>>  RecordWildCards
>>  ScopedTypeVariables
>>  TemplateHaskell
>> +TupleSections
>>  TypeFamilies
>>  ViewPatterns
>>  
>> diff --git a/website/config/routes b/website/config/routes
>> index 9e4e079..30fdef8 100644
>> --- a/website/config/routes
>> +++ b/website/config/routes
>> @@ -26,6 +26,8 @@
>>  /p/snowdrift SnowdriftProjectR GET
>>  /pledge/snowdrift PledgeSnowdriftR POST DELETE
>>  
>> +/discourse/sso DiscourseR GET
>> +
>>  -- ## Backward compatibility routes
>>  
>>  -- Prevents breakage of external links to the old wiki. See
>> diff --git a/website/config/settings.yml b/website/config/settings.yml
>> index 3c30c0f..e4e30bb 100644
>> --- a/website/config/settings.yml
>> +++ b/website/config/settings.yml
>> @@ -44,3 +44,7 @@ send-email: "_env:SD_EMAILS:false"
>>  # chreekat for assistance.
>>  stripe-secret-key: "_env:STRIPE_SECRET_KEY:"
>>  stripe-publishable-key: "_env:STRIPE_PUBLISHABLE_KEY:"
>> +
>> +# Discourse SSO
>> +discourse-url: "https://discourse.snowdrift.coop;
>> +discourse-sso-secret: ""
>> diff --git a/website/src/Application.hs b/website/src/Application.hs
>> index 8ccd6b2..761d3f8 100644
>> --- a/website/src/Application.hs
>> +++ b/website/src/Application.hs
>> @@ -35,6 +35,7 @@ import qualified Yesod.GitRev as G
>>  
>>  import Handler
>>  import Handler.Dashboard
>> +import Handler.Discourse
>>  import Handler.PaymentInfo
>>  import Handler.Pledge
>>  import Handler.Project
>> diff --git a/website/src/Discourse.hs b/website/src/Discourse.hs
>> new file mode 100644
>> index 000..cca0bb1
>> --- /dev/null
>> +++ b/website/src/Discourse.hs
>> @@ -0,0 +1,73 @@
>> +module Discourse where
>> +
>> +import Prelude
>> +
>> +import Crypto.Hash.Algorithms (SHA256)
>> +import Crypto.MAC.HMAC
>> +import Data.ByteArray.Encoding
>> 

Re: [Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-05 Thread Aaron Wolf
On 12/05/2016 11:34 AM, Bryan Richter wrote:
> Thanks for this. I think the best way to make use of this is to package
> up the logic specific to handling SSO requests in an independent
> library, so we can use it later.
> 
> That's not about you, or about this code. :) It's just that I am going
> to push back hard on having SSO be the second major feature we add to
> our website (or the third, or even the tenth).
> 
> We'll see where that discussion goes.
> 

I *strongly* like the idea of packaging SSO between Yesod app and
Discourse as an independent library we just use because that's in
general a great way to deal with generalized stuff like that.

But I want SSO implemented ASAP, immediately if possible. The
cleanliness and ease of managing everything about our use of Discourse
going forward and the site itself will be really nice if we start
Discourse with SSO from the get-go. I don't want all sorts of awkward
challenges in transition later after we have bunches of disconnected
users on each system and then try to push SSO then.

> On Mon, Dec 05, 2016 at 04:13:42AM +0200, fr33domlover wrote:
>> From: fr33domlover 
>>
>> ---
>>  website/Snowdrift.cabal  |  7 
>>  website/config/routes|  2 ++
>>  website/config/settings.yml  |  4 +++
>>  website/src/Application.hs   |  1 +
>>  website/src/Discourse.hs | 73 
>> 
>>  website/src/Handler/Discourse.hs | 55 ++
>>  website/src/Settings.hs  |  4 +++
>>  7 files changed, 146 insertions(+)
>>  create mode 100644 website/src/Discourse.hs
>>  create mode 100644 website/src/Handler/Discourse.hs
>>
>> diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
>> index 9c1664c..b5cfaa5 100644
>> --- a/website/Snowdrift.cabal
>> +++ b/website/Snowdrift.cabal
>> @@ -40,6 +40,7 @@ library
>>  Avatar
>>  Application
>>  Css
>> +Discourse
>>  Email
>>  Foundation
>>  Import
>> @@ -49,6 +50,7 @@ library
>>  Settings.StaticFiles
>>  Handler
>>  Handler.Dashboard
>> +Handler.Discourse
>>  Handler.PaymentInfo
>>  Handler.Pledge
>>  Handler.Project
>> @@ -74,6 +76,7 @@ library
>>  , bytestring >= 0.9 && < 0.11
>>  , classy-prelude >= 0.10.2
>>  , classy-prelude-yesod   >= 0.10.2
>> +, cryptonite
>>  , data-default
>>  , errors
>>  , esqueleto
>> @@ -82,8 +85,10 @@ library
>>  , formattable
>>  , hjsmin >= 0.1
>>  , http-client
>> +, http-types
>>  , lens
>>  , libravatar
>> +, memory
>>  , mime-mail
>>  , monad-logger   >= 0.3 && < 0.4
>>  , nonce
>> @@ -99,6 +104,7 @@ library
>>  , text   >= 0.11&& < 2.0
>>  , time
>>  , titlecase
>> +, transformers
>>  , unordered-containers
>>  , wai
>>  , wai-extra  >= 3.0 && < 3.1
>> @@ -122,6 +128,7 @@ library
>>  RecordWildCards
>>  ScopedTypeVariables
>>  TemplateHaskell
>> +TupleSections
>>  TypeFamilies
>>  ViewPatterns
>>  
>> diff --git a/website/config/routes b/website/config/routes
>> index 9e4e079..30fdef8 100644
>> --- a/website/config/routes
>> +++ b/website/config/routes
>> @@ -26,6 +26,8 @@
>>  /p/snowdrift SnowdriftProjectR GET
>>  /pledge/snowdrift PledgeSnowdriftR POST DELETE
>>  
>> +/discourse/sso DiscourseR GET
>> +
>>  -- ## Backward compatibility routes
>>  
>>  -- Prevents breakage of external links to the old wiki. See
>> diff --git a/website/config/settings.yml b/website/config/settings.yml
>> index 3c30c0f..e4e30bb 100644
>> --- a/website/config/settings.yml
>> +++ b/website/config/settings.yml
>> @@ -44,3 +44,7 @@ send-email: "_env:SD_EMAILS:false"
>>  # chreekat for assistance.
>>  stripe-secret-key: "_env:STRIPE_SECRET_KEY:"
>>  stripe-publishable-key: "_env:STRIPE_PUBLISHABLE_KEY:"
>> +
>> +# Discourse SSO
>> +discourse-url: "https://discourse.snowdrift.coop;
>> +discourse-sso-secret: ""
>> diff --git a/website/src/Application.hs b/website/src/Application.hs
>> index 8ccd6b2..761d3f8 100644
>> --- a/website/src/Application.hs
>> +++ b/website/src/Application.hs
>> @@ -35,6 +35,7 @@ import qualified Yesod.GitRev as G
>>  
>>  import Handler
>>  import Handler.Dashboard
>> +import Handler.Discourse
>>  import Handler.PaymentInfo
>>  import Handler.Pledge
>>  import Handler.Project
>> diff --git a/website/src/Discourse.hs b/website/src/Discourse.hs
>> new file mode 100644
>> index 000..cca0bb1
>> --- /dev/null
>> +++ b/website/src/Discourse.hs
>> @@ -0,0 +1,73 @@
>> +module Discourse where
>> +
>> +import Prelude
>> +
>> +import Crypto.Hash.Algorithms (SHA256)
>> +import Crypto.MAC.HMAC
>> +import Data.ByteArray.Encoding
>> 

[Snowdrift-dev] [PATCH] Discourse SSO support

2016-12-04 Thread fr33domlover
From: fr33domlover 

---
 website/Snowdrift.cabal  |  7 
 website/config/routes|  2 ++
 website/config/settings.yml  |  4 +++
 website/src/Application.hs   |  1 +
 website/src/Discourse.hs | 73 
 website/src/Handler/Discourse.hs | 55 ++
 website/src/Settings.hs  |  4 +++
 7 files changed, 146 insertions(+)
 create mode 100644 website/src/Discourse.hs
 create mode 100644 website/src/Handler/Discourse.hs

diff --git a/website/Snowdrift.cabal b/website/Snowdrift.cabal
index 9c1664c..b5cfaa5 100644
--- a/website/Snowdrift.cabal
+++ b/website/Snowdrift.cabal
@@ -40,6 +40,7 @@ library
 Avatar
 Application
 Css
+Discourse
 Email
 Foundation
 Import
@@ -49,6 +50,7 @@ library
 Settings.StaticFiles
 Handler
 Handler.Dashboard
+Handler.Discourse
 Handler.PaymentInfo
 Handler.Pledge
 Handler.Project
@@ -74,6 +76,7 @@ library
 , bytestring >= 0.9 && < 0.11
 , classy-prelude >= 0.10.2
 , classy-prelude-yesod   >= 0.10.2
+, cryptonite
 , data-default
 , errors
 , esqueleto
@@ -82,8 +85,10 @@ library
 , formattable
 , hjsmin >= 0.1
 , http-client
+, http-types
 , lens
 , libravatar
+, memory
 , mime-mail
 , monad-logger   >= 0.3 && < 0.4
 , nonce
@@ -99,6 +104,7 @@ library
 , text   >= 0.11&& < 2.0
 , time
 , titlecase
+, transformers
 , unordered-containers
 , wai
 , wai-extra  >= 3.0 && < 3.1
@@ -122,6 +128,7 @@ library
 RecordWildCards
 ScopedTypeVariables
 TemplateHaskell
+TupleSections
 TypeFamilies
 ViewPatterns
 
diff --git a/website/config/routes b/website/config/routes
index 9e4e079..30fdef8 100644
--- a/website/config/routes
+++ b/website/config/routes
@@ -26,6 +26,8 @@
 /p/snowdrift SnowdriftProjectR GET
 /pledge/snowdrift PledgeSnowdriftR POST DELETE
 
+/discourse/sso DiscourseR GET
+
 -- ## Backward compatibility routes
 
 -- Prevents breakage of external links to the old wiki. See
diff --git a/website/config/settings.yml b/website/config/settings.yml
index 3c30c0f..e4e30bb 100644
--- a/website/config/settings.yml
+++ b/website/config/settings.yml
@@ -44,3 +44,7 @@ send-email: "_env:SD_EMAILS:false"
 # chreekat for assistance.
 stripe-secret-key: "_env:STRIPE_SECRET_KEY:"
 stripe-publishable-key: "_env:STRIPE_PUBLISHABLE_KEY:"
+
+# Discourse SSO
+discourse-url: "https://discourse.snowdrift.coop;
+discourse-sso-secret: ""
diff --git a/website/src/Application.hs b/website/src/Application.hs
index 8ccd6b2..761d3f8 100644
--- a/website/src/Application.hs
+++ b/website/src/Application.hs
@@ -35,6 +35,7 @@ import qualified Yesod.GitRev as G
 
 import Handler
 import Handler.Dashboard
+import Handler.Discourse
 import Handler.PaymentInfo
 import Handler.Pledge
 import Handler.Project
diff --git a/website/src/Discourse.hs b/website/src/Discourse.hs
new file mode 100644
index 000..cca0bb1
--- /dev/null
+++ b/website/src/Discourse.hs
@@ -0,0 +1,73 @@
+module Discourse where
+
+import Prelude
+
+import Crypto.Hash.Algorithms (SHA256)
+import Crypto.MAC.HMAC
+import Data.ByteArray.Encoding
+import Data.ByteString (ByteString)
+import Data.Maybe (catMaybes)
+import Data.Text (Text, pack)
+import Data.Text.Encoding (encodeUtf8)
+import Network.HTTP.Types.URI (renderSimpleQuery)
+
+import qualified Data.ByteString as B (drop)
+
+import Model
+
+-- | Information we send back to Discourse once the user logs in through our
+-- UI.
+data UserInfo = UserInfo
+{ ssoEmail :: Text
+, ssoId:: UserId
+, ssoUsername  :: Maybe Text
+, ssoFullName  :: Maybe Text
+, ssoAvatarUrl :: Maybe Text
+, ssoBio   :: Maybe Text
+}
+
+-- | Type restricted convenience wrapper that computes our HMAC.
+hmacSHA256 :: ByteString -> ByteString -> HMAC SHA256
+hmacSHA256 = hmac
+
+-- | Given secret known in advance and payload given in the query, compute the
+-- HMAC-SHA256, to which Discourse refers as the signature.
+generateSig
+:: ByteString -- ^ Secret
+-> ByteString -- ^ Base64 encoded payload
+-> ByteString
+generateSig secret payload =
+convertToBase Base16 $ hmacGetDigest $ hmacSHA256 secret payload
+
+-- | This validates the payloads's authenticity (i.e. make sure it's really our
+-- trusted local Discourse instance) by using the signature as a proof that it
+-- knows the SSO secret. This is done by verifying that the HMAC-SHA256 of the
+-- secret and the payload is identical to the signature.
+validateSig
+:: ByteString -- ^ SSO secret, same one you specify in Discourse settings
+-> ByteString -- ^