export default {"/search": {"posts":[{"id":61693,"name":"Manoel Lemos","username":"mlemos","avatar_template":"/letter_avatar/mlemos/{size}/2.png","uploaded_avatar_id":null,"created_at":"2014-07-15T21:53:56.337-04:00","cooked":"

Gents, I'm using an very interesting tool to drive traffic across the different destinations of my community. One destination is the discussion forum running Discourse and other is the blog running WordPress.

\n\n

This tool is called Hello Bar and it is basically a bar that stays in the top of the site and links to other places or can be used to collect emails or social networks followers. It worked fine when I added the bar to my Wordpress Blog, but it failed (a bit) when added to my Discourse forum.

\n\n

You can check the two situations here:

\n\n\n\n

The error is that the Discourse page was suposed to be pushed down by the Hello Bar, but it isn't being fully pushed down. Then the layout is broken.

\n\n

Can anyone help me with this?

","post_number":1,"post_type":1,"updated_at":"2014-07-15T21:53:56.337-04:00","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":30,"incoming_link_count":2,"reads":40,"score":19.5,"yours":false,"topic_slug":null,"topic_id":17638,"display_username":"Manoel Lemos","primary_group_name":null,"version":2,"can_edit":false,"can_delete":false,"can_recover":false,"user_title":null,"actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":null},{"id":3,"count":0,"hidden":false,"can_act":null},{"id":4,"count":0,"hidden":false,"can_act":null},{"id":5,"count":0,"hidden":true,"can_act":null},{"id":6,"count":0,"hidden":false,"can_act":null},{"id":7,"count":0,"hidden":false,"can_act":null},{"id":8,"count":0,"hidden":false,"can_act":null}],"moderator":false,"admin":false,"staff":false,"user_id":7595,"hidden":false,"hidden_reason_id":null,"trust_level":1,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false,"blurb":"...the discussion forum running Discourse and other is the blog running WordPress. This tool is called Hello Bar and it is basically a bar that stays in the top of the site and links to other places or can be..."},{"id":56514,"name":"Ova Light","username":"ChrisOva","avatar_template":"/letter_avatar/chrisova/{size}/2.png","uploaded_avatar_id":null,"created_at":"2014-06-13T02:56:00.794-04:00","cooked":"

Do you know what are the elements whichI need to modify in the custom CSS editor in order for these parts of the forum to change color ?

\n\n

\n00000674.png2644x932 239 KB\n

\n\n

Is anyone here willing to work for a theme? (50$)

\n\n

Thanks for reading !

","post_number":1,"post_type":1,"updated_at":"2014-06-13T03:05:31.865-04:00","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":24,"incoming_link_count":1,"reads":31,"score":57.4,"yours":false,"topic_slug":null,"topic_id":16504,"display_username":"Ova Light","primary_group_name":null,"version":5,"can_edit":false,"can_delete":false,"can_recover":false,"user_title":null,"actions_summary":[{"id":2,"count":1,"hidden":false,"can_act":null},{"id":3,"count":0,"hidden":false,"can_act":null},{"id":4,"count":0,"hidden":false,"can_act":null},{"id":5,"count":0,"hidden":true,"can_act":null},{"id":6,"count":0,"hidden":false,"can_act":null},{"id":7,"count":0,"hidden":false,"can_act":null},{"id":8,"count":0,"hidden":false,"can_act":null}],"moderator":false,"admin":false,"staff":false,"user_id":10477,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":"downloaded local copies of images","can_view_edit_history":true,"wiki":false,"blurb":"Do you know what are the elements whichI need to modify in the custom CSS editor in order for these parts of the forum to change color ? 00000674.png 00000674.png 2644x932 239 KB Is anyone here wil..."},{"id":40862,"name":"Moe","username":"Moe","avatar_template":"/letter_avatar/moe/{size}/2.png","uploaded_avatar_id":null,"created_at":"2014-02-14T18:35:48.395-05:00","cooked":"

Hello,

\n\n

First and foremost, thanks to the developers of Discourse and everyone who is making great efforts in creating an awesome forum!

\n\n

I am a Discouse newbie and I am trying to do sso using the CAS sso auth plugin in https://github.com/eriko/cas_sso .

\n\n

I am currently making some experiments in authenticating users in Discourse using a test web app I am using as CAS server. I have set up discourse in a VM (vagrant) whereas the CAS server runs in the host machine. I have installed the sso plugin and set the values as indicated (i.e. cas_sso_host, cas_sso_port, etc, as it is a non-standard CAS server), with ssh disabled.

\n\n

One thing that I noticed is that it seems that there seems to be a cross domain protection acting, the following is a message that appears in the console when I go to login in vagrant discourse:

\n\n

Started GET \"/session/csrf\" for 10.0.2.2 at 2014-02-14 17:53:12 -0500\nProcessing by SessionController#csrf as */*\nCompleted 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms)
\n\n

Here 10.0.2.2 is the ip of the CAS server. This server is accessible from vagrant, I tested access to the CAS server URLs using lynx (everything works fine). I should likely mention also that cas_sso_login_url is set to /login and cas_sso_path is set to /cas in the Discourse sso plugin options. I have been also monitoring requests to /cas/login in the CAS server and none of my login attempts from Discourse have made it there.

\n\n

I am brand new to Discourse and Ruby, so I am having a bit of a struggle here and any insight would be greatly appreciated.

\n\n

Thank you very much in advance for your time and excellent disposition.

\n\n

Best regards,
Moe

","post_number":1,"post_type":1,"updated_at":"2014-02-14T18:35:48.395-05:00","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":20,"incoming_link_count":60,"reads":57,"score":357.4,"yours":false,"topic_slug":null,"topic_id":12731,"display_username":"Moe","primary_group_name":null,"version":2,"can_edit":false,"can_delete":false,"can_recover":false,"user_title":null,"actions_summary":[{"id":2,"count":1,"hidden":false,"can_act":null},{"id":3,"count":0,"hidden":false,"can_act":null},{"id":4,"count":0,"hidden":false,"can_act":null},{"id":5,"count":0,"hidden":true,"can_act":null},{"id":6,"count":0,"hidden":false,"can_act":null},{"id":7,"count":0,"hidden":false,"can_act":null},{"id":8,"count":0,"hidden":false,"can_act":null}],"moderator":false,"admin":false,"staff":false,"user_id":8596,"hidden":false,"hidden_reason_id":null,"trust_level":1,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false,"blurb":"Hello, First and foremost, thanks to the developers of Discourse and everyone who is making great efforts..."},{"id":18691,"name":"Sam Saffron","username":"sam","avatar_template":"/letter_avatar/sam/{size}/2.png","uploaded_avatar_id":null,"created_at":"2013-05-30T21:17:09.213-04:00","cooked":"

For the feature I was working on yesterday, @codinghorror wanted a rather complex sentence.

\n\n

There is 1 unread and 9 new topics remaining, or browse other topics in [category]
\n\n

This seemingly simple sentence was a royal nightmare to localize with our existing localization system. Think through all the permutations:

\n\n

\"There are 2 unread and 9 new topics remaining, or browse other topics in [category]\"
\"There are 2 unread and 1 new topic remaining, or browse other topics in [category]\"
\"There is 1 unread and 1 new topic remaining, or browse other topics in [category]\"

\n\n

Trouble with our current system was that you have no sane way of building these kind of sentences, see: http://stackoverflow.com/questions/16825932/clean-pattern-for-localizing-sentences-in-rails-i18n , you can only easily localize one count in a non compound sentence.

\n\n

To alleviate this I introduce a new mechanism that is available (optionally) client side. The above sentence is localized using:

\n\n

There {UNREAD, plural, \n   one {is <a href='/unread'>1 unread</a>} \n   other {are <a href='/unread'># unread</a>}\n} and {NEW, plural, \n  one {<a href='/new'>1 new</a> topic} \n  other {<a href='/new'># new</a> topics}} remaining, or browse other topics in {catLink}
\n\n

The client localization file has a special rule, if a key ends with _MF it is interpreted as a MessageFormat message, then to access it on the client you use:

\n\n

I18n.messageFormat(\"topic.read_more_in_category_MF\", {\"UNREAD\": unreadTopics, \"NEW\": newTopics, catLink: opts.catLink})
\n\n

You can see a few other examples here:

\n\n

\n\n

We do not plan at the moment to move to message format style localization everywhere, however it is nice to have this extra bit of flexibility that lets us generate interesting sentences.

\n\n

On a technical note, this feature adds almost no weight to the client side JavaScript, all message format strings are pre-compiled into a JavaScript function with no external dependencies. The tricks used can be viewed here: https://github.com/discourse/discourse/blob/master/lib/js_locale_helper.rb

\n\n

1 minute Message Format primer

\n\n

f = \"hello\"\nf() => \"hello\"\n\nf = \"hello {WORLD}\"\nf(WORLD: \"world\") => \"hello world\" \nf(WORLD: \"other world\") => \"hello other world\" \n\nf = \"I have {HATS, plural, one {one hat} other {# hats}}\"\nf(HATS: 1) => \"I have one hat\"\nf(HATS: 10) => \"I have 10 hats\" \n\nf = \"I am a {GENDER, select, male {boy}, female {girl}}\"\nf(GENDER: \"male\") => \"I am a boy\"\nf(GENDER: \"female\") => \"I am a girl\"
\n\n

Our plan for now is to use this strategically, however it is worth noting that this gives more flexibility in localization, for example in czech, the plural form is rather interesting as @kuba could attest :

\n\n

MessageFormat.locale.cs = function (n) {\n  if (n == 1) {\n    return 'one';\n  }\n  if (n == 2 || n == 3 || n == 4) {\n    return 'few';\n  }\n  return 'other';\n};
\n\n

Message Format supports this fine, built in.

\n\n

f = \"I have {HATS, plural, one {one hat} other {# hats} few {# few hats}}\"
","post_number":1,"post_type":1,"updated_at":"2013-05-30T21:31:57.135-04:00","reply_count":1,"reply_to_post_number":null,"quote_count":0,"avg_time":33,"incoming_link_count":44,"reads":68,"score":450.25,"yours":false,"topic_slug":null,"topic_id":7035,"display_username":"Sam Saffron","primary_group_name":"discourse","version":3,"can_edit":false,"can_delete":false,"can_recover":false,"user_title":"co-founder","actions_summary":[{"id":2,"count":6,"hidden":false,"can_act":null},{"id":3,"count":0,"hidden":false,"can_act":null},{"id":4,"count":0,"hidden":false,"can_act":null},{"id":5,"count":0,"hidden":true,"can_act":null},{"id":6,"count":0,"hidden":false,"can_act":null},{"id":7,"count":0,"hidden":false,"can_act":null},{"id":8,"count":0,"hidden":false,"can_act":null}],"moderator":true,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":3,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false,"blurb":"...hub.com/discourse/discourse/blob/master/lib/js_locale_helper.rb 1 minute Message Format primer f = \"hello\" f() = > \"hello\" f = \"hello {WORLD}\" f(WORLD: \"world\") = > \"hello world\" f(WORLD: \"other world\") =..."},{"id":41969,"name":"Sam Saffron","username":"sam","avatar_template":"/letter_avatar/sam/{size}/2.png","uploaded_avatar_id":null,"created_at":"2014-02-25T03:30:34.423-05:00","cooked":"

Discourse now ships with official hooks to perform auth offsite.

\n\n

The Problem

\n\n

Many sites wish to integrate with a Discourse site, however want to keep all user registration in a separate site. In such a setup all Login operations should be outsourced to a different site.

\n\n

What if I would like SSO in conjunction with existing auth?

\n\n

The intention around SSO is to replace Discourse authentication, if you would like to add a new provider see existing plugins such as: https://meta.discourse.org/t/vk-com-login-vkontakte/12987

\n\n

Enabling SSO

\n\n

To enable single sign on you have 3 settings you need to fill out:

\n\n

\nPasted image798x240 16.8 KB\n

\n\n

enable_sso : must be enabled, global switch
sso_url: the offsite URL users will be sent to when attempting to log on
sso_secret: a secret string used to hash SSO payloads. Ensures payloads are authentic.

\n\n

Once enable_sso is set to true:

\n\n\n\n

What if you check it by mistake?

\n\n

If you check enable_sso by mistake and need to revert to the original state and no longer have access to the admin panel

\n\n

run:

\n\n

./launcher enter app\nrails c\nirb > SiteSetting.enable_sso = false\nirb > exit\nexit
\n\n

Implementing SSO on your site

\n\n

Discourse will redirect clients to sso_url with a signed payload: (say sso_url is https://somesite.com/sso)

\n\n

You will receive incoming traffic with the following

\n\n

https://somesite.com/sso?sso=PAYLOAD&sig=SIG

\n\n

The payload is a Base64 encoded string comprising of a nonce. The payload is always a valid querystring.

\n\n

For example, if the nonce is ABCD. raw_payload will be:

\n\n

nonce=ABCD, this raw payload is base 64 encoded.

\n\n

The endpoint being called must

\n\n
    \n
  1. Validate the signature, ensure that HMAC-SHA256 of sso_secret, PAYLOAD is equal to the sig
  2. \n
  3. Perform whatever authentication it has to
  4. \n
  5. Create a new payload with nonce, email, external_id and optionally (username, name)
  6. \n
  7. Base64 encode the payload
  8. \n
  9. Calculate a HMAC-SHA256 hash of the using sso_secret as the key and Base64 encoded payload as text
  10. \n
  11. Redirect back to http://discourse_site/session/sso_login?sso=payload&sig=sig\n
  12. \n
\n\n

Discourse will validate that the nonce is valid (if valid it will expire it right away so it can no longer be used) it will attempt to:

\n\n
    \n
  1. Log the user on by looking up an already associated external_id in the SingleSignOnRecord model
  2. \n
  3. Log the user on by using the email provided (updating external_id)
  4. \n
  5. Create a new account for the user providing (email, username, name) updating external_id
  6. \n
\n\n

Security concerns

\n\n

The nonce (one time token) will expire automatically after 10 minutes. This means that as soon as the user is redirected to your site they have 10 minutes to log in / create a new account.

\n\n

The protocol is safe against replay attacks as nonce may only be used once.

\n\n

Reference implementation

\n\n

Discourse contains a reference implementation of the SSO class:

\n\n\n\n\n

A trivial implementation would be:

\n\n

class DiscourseSsoController < ApplicationController\n  def sso\n    secret = \"MY_SECRET_STRING\"\n    sso = SingleSignOn.parse(request.query_string, secret)\n    sso.email = \"user@email.com\"\n    sso.name = \"Bill Hicks\"\n    sso.username = \"bill@hicks.com\"\n    sso.external_id = \"123\" # unique to your application\n    sso.sso_secret = secret\n\n    redirect_to sso.to_url(\"http://l.discourse/session/sso_login\")\n  end\nend
\n\n

Transitioning to and from single sign on.

\n\n

The system always trusts emails provided by the single sign on endpoint. This means that if you had an existing account in the past on Discourse with SSO disabled, SSO will simply re-use it and avoid creating a new account.

\n\n

If you ever turn off SSO, users will be able to reset passwords and gain access back to their accounts.

\n\n

Real world example:

\n\n

Given the following settings:

\n\n

Discourse domain: http://discuss.example.com
SSO url : http://www.example.com/discourse/sso
SSO secret: d836444a9e4084d5b224a60c208dce14

\n\n

User attempt to login

\n\n\n\n

Finally browser is redirected to:

\n\n

http://www.example.com/discourse/sso?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A&sig=2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56

\n\n

On the other end

\n\n
    \n
  1. Payload is validated using HMAC-SHA256, if the sig mismatches, process aborts.
  2. \n
  3. By reversing the steps above nonce is extracted.
  4. \n
\n\n

User logs in:

\n\n

name: sam\nexternal_id: hello123\nemail: test@test.com\nusername: samsam
\n\n\n\n

nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123

\n\n

order does not matter, values are URL encoded

\n\n\n\n

\"bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z\\nYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl\\ncm5hbF9pZD1oZWxsbzEyMw==\\n

\n\n\n\n

bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A

\n\n\n\n

1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b

\n\n\n\n

http://discuss.example.com/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A&sig=1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b

\n\n

Future work

\n\n\n\n

Advanced Features

\n\n\n\n

Updates:

\n\n

2-Feb-2014

\n\n\n\n

4-April-2014

\n\n\n\n

24-April-2014

\n\n\n\n

01-August-2014

\n\n","post_number":1,"post_type":1,"updated_at":"2014-08-01T17:44:20.164-04:00","reply_count":5,"reply_to_post_number":null,"quote_count":0,"avg_time":41,"incoming_link_count":2284,"reads":536,"score":12367.25,"yours":false,"topic_slug":null,"topic_id":13045,"display_username":"Sam Saffron","primary_group_name":"discourse","version":12,"can_edit":false,"can_delete":false,"can_recover":false,"user_title":"co-founder","actions_summary":[{"id":2,"count":41,"hidden":false,"can_act":null},{"id":3,"count":0,"hidden":false,"can_act":null},{"id":4,"count":0,"hidden":false,"can_act":null},{"id":5,"count":0,"hidden":true,"can_act":null},{"id":6,"count":0,"hidden":false,"can_act":null},{"id":7,"count":0,"hidden":false,"can_act":null},{"id":8,"count":0,"hidden":false,"can_act":null}],"moderator":true,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":3,"deleted_at":null,"user_deleted":false,"edit_reason":"","can_view_edit_history":true,"wiki":false,"blurb":"...ocess aborts. By reversing the steps above nonce is extracted. User logs in: name: sam external_id: hello123 email: test@test.com username: samsam Unsigned payload is generated: nonce=cb68251eefb5211e58c00..."}],"topics":[{"id":17638,"title":"Hello Bar integration issues","fancy_title":"Hello Bar integration issues","slug":"hello-bar-integration-issues","posts_count":5,"reply_count":2,"highest_post_number":5,"image_url":null,"created_at":"2014-07-15T21:53:56.226-04:00","last_posted_at":"2014-07-15T22:51:01.719-04:00","bumped":true,"bumped_at":"2014-07-15T22:01:39.716-04:00","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"views":84,"like_count":1,"has_summary":false,"archetype":"regular","last_poster_username":null,"category_id":6,"posters":[]},{"id":16504,"title":"Hello, I have two questions :D","fancy_title":"Hello, I have two questions :D","slug":"hello-i-have-two-questions-d","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/uploads/default/_optimized/b35/289/b2338e0876_690x243.png","created_at":"2014-06-13T02:56:00.695-04:00","last_posted_at":"2014-06-13T06:27:28.903-04:00","bumped":true,"bumped_at":"2014-06-13T06:27:28.903-04:00","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"views":97,"like_count":3,"has_summary":false,"archetype":"regular","last_poster_username":null,"category_id":14,"posters":[]},{"id":12731,"title":"CAS sso auth plugin question","fancy_title":"CAS sso auth plugin question","slug":"cas-sso-auth-plugin-question","posts_count":15,"reply_count":5,"highest_post_number":15,"image_url":null,"created_at":"2014-02-14T18:35:48.242-05:00","last_posted_at":"2014-02-25T20:50:16.306-05:00","bumped":true,"bumped_at":"2014-02-25T20:50:16.306-05:00","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"views":440,"like_count":2,"has_summary":false,"archetype":"regular","last_poster_username":null,"category_id":5,"posters":[]},{"id":7035,"title":"Message Format support for localization","fancy_title":"Message Format support for localization","slug":"message-format-support-for-localization","posts_count":7,"reply_count":5,"highest_post_number":7,"image_url":"http://meta.discourse.org/assets/favicons/github-65cd2c8ba8283c55eca7f9e257fa7604.png","created_at":"2013-05-30T21:17:08.971-04:00","last_posted_at":"2014-09-03T03:11:36.653-04:00","bumped":true,"bumped_at":"2014-09-03T03:11:36.653-04:00","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"views":699,"like_count":10,"has_summary":false,"archetype":"regular","last_poster_username":null,"category_id":2,"posters":[]},{"id":13045,"title":"Official Single-Sign-On for Discourse","fancy_title":"Official Single-Sign-On for Discourse","slug":"official-single-sign-on-for-discourse","posts_count":61,"reply_count":37,"highest_post_number":64,"image_url":"/uploads/default/_optimized/07c/3bf/3fa1d69ceb_690x207.png","created_at":"2014-02-25T03:30:34.321-05:00","last_posted_at":"2014-08-01T17:44:56.523-04:00","bumped":true,"bumped_at":"2014-08-07T13:27:14.684-04:00","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"views":13377,"like_count":74,"has_summary":true,"archetype":"regular","last_poster_username":null,"category_id":10,"posters":[]}],"users":[{"id":3836,"username":"HelloWorld","uploaded_avatar_id":null,"avatar_template":"/letter_avatar/helloworld/{size}/2.png"},{"id":6315,"username":"instagra","uploaded_avatar_id":null,"avatar_template":"/letter_avatar/instagra/{size}/2.png"},{"id":1743,"username":"hello_jmk","uploaded_avatar_id":null,"avatar_template":"/letter_avatar/hello_jmk/{size}/2.png"},{"id":9349,"username":"hellocate","uploaded_avatar_id":null,"avatar_template":"/letter_avatar/hellocate/{size}/2.png"},{"id":10596,"username":"hellooperator","uploaded_avatar_id":null,"avatar_template":"/letter_avatar/hellooperator/{size}/2.png"}],"categories":[],"grouped_search_result":{"more_posts":true,"more_users":null,"more_categories":null,"post_ids":[61693,56514,40862,18691,41969],"user_ids":[3836,6315,1743,9349,10596],"category_ids":[]}}};