Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

integrating train 2011.11.03

  • Loading branch information...
commit c589aec44697b22981a64d66e8ca5091a569c3df 2 parents 44ff6a9 + 6d5c0c3
@lloyd lloyd authored
Showing with 3,137 additions and 1,710 deletions.
  1. +1 −0  .gitignore
  2. +23 −0 ChangeLog
  3. +378 −0 bin/browserid
  4. +60 −10 verifier/run.js → bin/keysigner
  5. +7 −8 performance/run.js → bin/load_gen
  6. +44 −43 verifier/app.js → bin/verifier
  7. +0 −330 browserid/app.js
  8. +0 −117 browserid/static/dialog/resources/error-messages.js
  9. +0 −54 browserid/static/dialog/views/authenticate.ejs
  10. +0 −134 browserid/tests/lib/start-stop.js
  11. +0 −40 browserid/views/index.ejs
  12. 0  { → docs}/DEPLOYMENT.md
  13. 0  performance/README.md → docs/LOAD_GENERATION.md
  14. 0  { → docs}/ORGANIZATION.md
  15. 0  {rp → example}/index.html
  16. 0  {rp → example}/jquery-min.js
  17. +5 −6 {browserid/lib → lib/browserid}/ca.js
  18. +5 −4 {browserid/lib → lib/browserid}/email.js
  19. 0  {browserid/lib → lib/browserid}/fake_verification.js
  20. +49 −0 lib/browserid/http_forward.js
  21. 0  {browserid/lib → lib/browserid}/prove_template.txt
  22. +87 −77 {browserid/lib → lib/browserid}/wsapi.js
  23. +81 −49 {libs → lib}/configuration.js
  24. +4 −3 {browserid → }/lib/db.js
  25. +89 −51 browserid/lib/db_json.js → lib/db/json.js
  26. +22 −11 browserid/lib/db_mysql.js → lib/db/mysql.js
  27. +38 −0 lib/heartbeat.js
  28. +10 −0 {browserid → }/lib/httputils.js
  29. +38 −33 browserid/run.js → lib/keysigner/ca.js
  30. 0  {performance/lib → lib/load_gen}/add_email.js
  31. 0  {performance/lib → lib/load_gen}/include_only.js
  32. 0  {performance/lib → lib/load_gen}/reauth.js
  33. 0  {performance/lib → lib/load_gen}/reset_pass.js
  34. 0  {performance/lib → lib/load_gen}/signin.js
  35. 0  {performance/lib → lib/load_gen}/signup.js
  36. 0  {performance/lib → lib/load_gen}/test.js
  37. 0  {performance/lib → lib/load_gen}/user_db.js
  38. +12 −6 {libs → lib}/logging.js
  39. 0  {libs → lib}/metrics.js
  40. +2 −5 {libs → lib}/secrets.js
  41. +126 −0 lib/shutdown.js
  42. +32 −29 browserid/static/js/pages/forgot.js → lib/validate.js
  43. +6 −10 {verifier/lib → lib/verifier}/certassertion.js
  44. +2 −2 {libs → lib}/wsapi_client.js
  45. +0 −7 libs/heartbeat.js
  46. +0 −106 libs/substitute.js
  47. +4 −2 package.json
  48. +6 −4 {browserid → resources}/.gitignore
  49. 0  {browserid → resources}/assets/account-buttons.png
  50. 0  {browserid → resources}/assets/browserID-135x35.png
  51. 0  {browserid → resources}/assets/browserID-366x72.png
  52. 0  {browserid → resources}/assets/browserID-80x20.png
  53. 0  {browserid → resources}/assets/browserID-buttons.psd
  54. 0  {browserid → resources}/assets/browserID-logo.eps
  55. 0  {browserid → resources}/static/.well-known/host-meta
  56. 0  {browserid → resources}/static/css/m.css
  57. 0  {browserid → resources}/static/css/sil.ttf
  58. +13 −1 {browserid → resources}/static/css/style.css
  59. 0  {browserid → resources}/static/css/ts.ttf
  60. +16 −24 {browserid → resources}/static/dialog/controllers/authenticate_controller.js
  61. 0  {browserid → resources}/static/dialog/controllers/checkregistration_controller.js
  62. +34 −9 {browserid → resources}/static/dialog/controllers/dialog_controller.js
  63. +17 −3 {browserid → resources}/static/dialog/controllers/page_controller.js
  64. +12 −1 {browserid → resources}/static/dialog/controllers/pickemail_controller.js
  65. +9 −3 {browserid → resources}/static/dialog/css/m.css
  66. +26 −5 {browserid → resources}/static/dialog/css/popup.css
  67. +3 −1 {browserid → resources}/static/dialog/dialog.js
  68. 0  {browserid → resources}/static/dialog/funcunit.html
  69. 0  {browserid → resources}/static/dialog/mozilla.png
  70. +22 −1 {browserid → resources}/static/dialog/qunit.html
  71. 0  {browserid → resources}/static/dialog/register_iframe.html
  72. 0  {browserid → resources}/static/dialog/register_iframe.js
  73. 0  {browserid → resources}/static/dialog/resources/base64.js
  74. 0  {browserid → resources}/static/dialog/resources/browser-support.js
  75. 0  {browserid → resources}/static/dialog/resources/browserid-extensions.js
  76. 0  {browserid → resources}/static/dialog/resources/browserid.js
  77. +65 −63 {browserid → resources}/static/dialog/resources/channel.js
  78. +59 −38 browserid/static/relay/relay.js → resources/static/dialog/resources/error-messages.js
  79. 0  {browserid → resources}/static/dialog/resources/jschannel.js
  80. +45 −9 {browserid → resources}/static/dialog/resources/network.js
  81. 0  {browserid → resources}/static/dialog/resources/storage.js
  82. +20 −8 {browserid → resources}/static/dialog/resources/tooltip.js
  83. 0  {browserid → resources}/static/dialog/resources/underscore-min.js
  84. +33 −22 {browserid → resources}/static/dialog/resources/user.js
  85. +11 −3 {browserid → resources}/static/dialog/resources/validation.js
  86. 0  {browserid → resources}/static/dialog/resources/wait-messages.js
  87. 0  {browserid → resources}/static/dialog/scripts/build.html
  88. 0  {browserid → resources}/static/dialog/scripts/build.js
  89. 0  {browserid → resources}/static/dialog/scripts/clean.js
  90. 0  {browserid → resources}/static/dialog/scripts/docs.js
  91. 0  {browserid → resources}/static/dialog/test/funcunit/dialog_test.js
  92. 0  {browserid → resources}/static/dialog/test/funcunit/funcunit.js
  93. +94 −0 resources/static/dialog/test/qunit/controllers/authenticate_controller_unit_test.js
  94. +122 −0 resources/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js
  95. +35 −3 {browserid → resources}/static/dialog/test/qunit/controllers/page_controller_unit_test.js
  96. +96 −0 resources/static/dialog/test/qunit/controllers/pickemail_controller_unit_test.js
  97. 0  {browserid → resources}/static/dialog/test/qunit/dialog_test.js
  98. 0  {browserid → resources}/static/dialog/test/qunit/include_unit_test.js
  99. +2 −2 {browserid/static/dialog/test/qunit → resources/static/dialog/test/qunit/js}/browserid_unit_test.js
  100. +81 −0 resources/static/dialog/test/qunit/js/page_helpers_unit_test.js
  101. +39 −0 resources/static/dialog/test/qunit/mocks/mocks.js
  102. +162 −0 resources/static/dialog/test/qunit/mocks/xhr.js
  103. +37 −31 {browserid → resources}/static/dialog/test/qunit/pages/add_email_address_test.js
  104. +129 −0 resources/static/dialog/test/qunit/pages/forgot_unit_test.js
  105. +18 −6 {browserid → resources}/static/dialog/test/qunit/qunit.js
  106. +183 −0 resources/static/dialog/test/qunit/relay/relay_unit_test.js
  107. 0  {browserid → resources}/static/dialog/test/qunit/resources/browser-support_unit_test.js
  108. +162 −0 resources/static/dialog/test/qunit/resources/channel_unit_test.js
  109. +57 −120 {browserid → resources}/static/dialog/test/qunit/resources/network_unit_test.js
  110. 0  {browserid → resources}/static/dialog/test/qunit/resources/storage_unit_test.js
  111. +80 −0 resources/static/dialog/test/qunit/resources/tooltip_unit_test.js
  112. +131 −210 {browserid → resources}/static/dialog/test/qunit/resources/user_unit_test.js
  113. +76 −2 {browserid → resources}/static/dialog/test/qunit/resources/validation_unit_test.js
  114. +60 −0 resources/static/dialog/views/authenticate.ejs
  115. 0  {browserid → resources}/static/dialog/views/confirmemail.ejs
  116. +42 −0 resources/static/dialog/views/error.ejs
  117. +8 −0 resources/static/dialog/views/offline.ejs
  118. +7 −4 {browserid → resources}/static/dialog/views/pickemail.ejs
  119. 0  {browserid → resources}/static/dialog/views/testBodyTemplate.ejs
  120. 0  {browserid → resources}/static/dialog/views/wait.ejs
  121. 0  {browserid → resources}/static/favicon.ico
  122. 0  {browserid/static/steal/get/test → resources/static/funcunit}/.gitignore
  123. 0  {browserid/static/steal/get/test → resources/static/funcunit}/.gitmodules
  124. 0  {browserid/static/steal/get/test → resources/static/funcunit}/README
  125. 0  {browserid → resources}/static/funcunit/autosuggest/auto_suggest.js
  126. 0  {browserid → resources}/static/funcunit/autosuggest/autosuggest.css
  127. 0  {browserid → resources}/static/funcunit/autosuggest/autosuggest.html
  128. 0  {browserid → resources}/static/funcunit/autosuggest/autosuggest.js
  129. 0  {browserid → resources}/static/funcunit/autosuggest/autosuggest_test.js
  130. 0  {browserid → resources}/static/funcunit/autosuggest/funcunit.html
  131. 0  {browserid → resources}/static/funcunit/build.js
  132. 0  {browserid → resources}/static/funcunit/dependencies.json
  133. 0  {browserid → resources}/static/funcunit/docs.html
  134. 0  {browserid → resources}/static/funcunit/drivers/selenium.js
  135. 0  {browserid → resources}/static/funcunit/drivers/standard.js
  136. 0  {browserid → resources}/static/funcunit/envjs
  137. 0  {browserid → resources}/static/funcunit/envjs.bat
  138. 0  {browserid → resources}/static/funcunit/funcunit.html
  139. 0  {browserid → resources}/static/funcunit/funcunit.js
  140. 0  {browserid → resources}/static/funcunit/generate_docs.html
  141. 0  {browserid → resources}/static/funcunit/index.html
  142. 0  {browserid → resources}/static/funcunit/java/extensions/fakesteal.js
  143. 0  {browserid → resources}/static/funcunit/java/extensions/wrapped.js
  144. 0  {browserid → resources}/static/funcunit/java/selenium-java-client-driver.jar
  145. 0  {browserid → resources}/static/funcunit/java/selenium-server-standalone-2.0b3.jar
  146. 0  {browserid → resources}/static/funcunit/java/user-extensions.js
  147. 0  {browserid → resources}/static/funcunit/loader.js
  148. 0  {browserid → resources}/static/funcunit/pages/example.js
  149. 0  {browserid → resources}/static/funcunit/pages/follow.js
  150. 0  {browserid → resources}/static/funcunit/pages/init.js
  151. 0  {browserid → resources}/static/funcunit/pages/mastering.js
  152. 0  {browserid → resources}/static/funcunit/pages/selenium.js
  153. 0  {browserid → resources}/static/funcunit/pages/setup.js
  154. 0  {browserid → resources}/static/funcunit/pages/standalone.js
  155. 0  {browserid → resources}/static/funcunit/pages/writing.js
  156. 0  {browserid → resources}/static/funcunit/qunit.html
  157. 0  {browserid/static/funcunit/syn/resources → resources/static/funcunit}/qunit/qunit.css
  158. 0  {browserid → resources}/static/funcunit/qunit/qunit.js
  159. 0  {browserid → resources}/static/funcunit/qunit/rhino/rhino.js
  160. 0  {browserid → resources}/static/funcunit/qunit/test/qunit.html
  161. 0  {browserid → resources}/static/funcunit/qunit/test/test.js
  162. 0  {browserid/static/funcunit/syn → resources/static/funcunit}/resources/jquery.js
  163. 0  {browserid → resources}/static/funcunit/resources/json.js
  164. 0  {browserid → resources}/static/funcunit/resources/selector.js
  165. 0  {browserid → resources}/static/funcunit/resources/selenium_start.js
  166. 0  {browserid → resources}/static/funcunit/scripts/run.js
  167. 0  {browserid → resources}/static/funcunit/settings.js
  168. 0  {browserid → resources}/static/funcunit/summary.ejs
  169. 0  {browserid → resources}/static/funcunit/syn/.gitignore
  170. 0  {browserid → resources}/static/funcunit/syn/README
  171. 0  {browserid → resources}/static/funcunit/syn/browsers.js
  172. 0  {browserid → resources}/static/funcunit/syn/build.js
  173. 0  {browserid → resources}/static/funcunit/syn/demo.html
  174. 0  {browserid → resources}/static/funcunit/syn/demo/record.js
  175. 0  {browserid → resources}/static/funcunit/syn/drag/drag.html
  176. 0  {browserid → resources}/static/funcunit/syn/drag/drag.js
  177. 0  {browserid → resources}/static/funcunit/syn/drag/qunit.html
  178. 0  {browserid → resources}/static/funcunit/syn/drag/test/qunit/drag_test.js
  179. 0  {browserid → resources}/static/funcunit/syn/drag/test/qunit/qunit.js
  180. 0  {browserid → resources}/static/funcunit/syn/key.js
  181. 0  {browserid → resources}/static/funcunit/syn/mouse.js
  182. 0  {browserid → resources}/static/funcunit/syn/qunit.html
  183. 0  {browserid → resources}/static/funcunit/syn/recorder.html
  184. 0  {browserid → resources}/static/funcunit/syn/resources/jquery.event.drag.js
  185. 0  {browserid → resources}/static/funcunit/syn/resources/jquery.event.drop.js
  186. 0  {browserid/static/funcunit → resources/static/funcunit/syn}/resources/jquery.js
  187. 0  {browserid/static/funcunit → resources/static/funcunit/syn/resources}/qunit/qunit.css
  188. 0  {browserid → resources}/static/funcunit/syn/resources/qunit/qunit.js
  189. 0  {browserid → resources}/static/funcunit/syn/syn.js
  190. 0  {browserid → resources}/static/funcunit/syn/synthetic.html
  191. 0  {browserid → resources}/static/funcunit/syn/synthetic.js
  192. 0  {browserid → resources}/static/funcunit/syn/test/clickbasic.html
  193. 0  {browserid → resources}/static/funcunit/syn/test/qunit/h3.html
  194. 0  {browserid → resources}/static/funcunit/syn/test/qunit/key_test.js
  195. 0  {browserid → resources}/static/funcunit/syn/test/qunit/mouse_test.js
  196. 0  {browserid → resources}/static/funcunit/syn/test/qunit/page1.html
  197. 0  {browserid → resources}/static/funcunit/syn/test/qunit/page2.html
  198. 0  {browserid → resources}/static/funcunit/syn/test/qunit/qunit.js
  199. 0  {browserid → resources}/static/funcunit/syn/test/qunit/syn_test.js
  200. 0  {browserid → resources}/static/funcunit/syn/test/submit.html
  201. 0  {browserid → resources}/static/funcunit/syn/test/submitted.html
  202. 0  {browserid → resources}/static/funcunit/template.html
  203. 0  {browserid → resources}/static/funcunit/test/drag.html
  204. 0  {browserid → resources}/static/funcunit/test/findclosest.html
  205. 0  {browserid → resources}/static/funcunit/test/funcunit/find_closest_test.js
  206. 0  {browserid → resources}/static/funcunit/test/funcunit/funcunit.js
  207. 0  {browserid → resources}/static/funcunit/test/funcunit/funcunit_test.js
  208. 0  {browserid → resources}/static/funcunit/test/funcunit/open_test.js
  209. 0  {browserid → resources}/static/funcunit/test/funcunit/protodrag_test.js
  210. 0  {browserid → resources}/static/funcunit/test/funcunit/syn_test.js
  211. 0  {browserid → resources}/static/funcunit/test/jquery.event.drag.js
  212. 0  {browserid → resources}/static/funcunit/test/jquery.event.drop.js
  213. 0  {browserid → resources}/static/funcunit/test/jquery.js
  214. 0  {browserid → resources}/static/funcunit/test/myapp.html
  215. 0  {browserid → resources}/static/funcunit/test/myotherapp.html
  216. 0  {browserid → resources}/static/funcunit/test/protodrag/dragdrop.js
  217. 0  {browserid → resources}/static/funcunit/test/protodrag/effects.js
  218. 0  {browserid → resources}/static/funcunit/test/protodrag/funcunit_test.js
  219. 0  {browserid → resources}/static/funcunit/test/protodrag/myapp.html
  220. 0  {browserid → resources}/static/funcunit/test/protodrag/prototype.js
  221. 0  {browserid → resources}/static/funcunit/test/protodrag/scriptaculous.js
  222. 0  {browserid → resources}/static/funcunit/test/qunit/qunit.js
  223. 0  {browserid → resources}/static/funcunit/test/run.js
  224. 0  {browserid → resources}/static/funcunit/update
  225. 0  {browserid → resources}/static/i/a_better_way.png
  226. 0  {browserid → resources}/static/i/arrow.png
  227. 0  {browserid → resources}/static/i/bg.png
  228. 0  {browserid → resources}/static/i/blink.gif
  229. 0  {browserid → resources}/static/i/browserid_logo_lil.png
  230. 0  {browserid → resources}/static/i/browserid_logo_sm.png
  231. 0  {browserid → resources}/static/i/card.png
  232. 0  {browserid → resources}/static/i/check.png
  233. 0  {browserid → resources}/static/i/count.png
  234. 0  {browserid → resources}/static/i/firefox_logo.png
  235. 0  {browserid → resources}/static/i/hint.png
  236. 0  {browserid → resources}/static/i/icon.png
  237. 0  {browserid → resources}/static/i/labs-logo-small.png
  238. 0  {browserid → resources}/static/i/lock.png
  239. 0  {browserid → resources}/static/i/sign_in_blue.png
  240. 0  {browserid → resources}/static/i/sign_in_green.png
  241. 0  {browserid → resources}/static/i/sign_in_grey.png
  242. 0  {browserid → resources}/static/i/sign_in_orange.png
  243. 0  {browserid → resources}/static/i/sign_in_red.png
  244. 0  {browserid → resources}/static/i/slit.png
  245. 0  {browserid → resources}/static/i/sprite.png
  246. 0  {browserid → resources}/static/i/sunny.png
  247. 0  {browserid → resources}/static/i/times.gif
  248. 0  {browserid → resources}/static/i/tutorial_1.png
  249. 0  {browserid → resources}/static/i/tutorial_2.png
  250. 0  {browserid → resources}/static/i/tutorial_3.png
  251. 0  {browserid → resources}/static/include.js
  252. 0  {browserid → resources}/static/jquery/.gitignore
  253. 0  {browserid → resources}/static/jquery/README
  254. 0  {browserid → resources}/static/jquery/build.js
  255. 0  {browserid → resources}/static/jquery/buildAll.js
  256. 0  {browserid → resources}/static/jquery/class/class.html
  257. 0  {browserid → resources}/static/jquery/class/class.js
  258. 0  {browserid → resources}/static/jquery/class/class_test.js
  259. 0  {browserid → resources}/static/jquery/class/qunit.html
  260. 0  {browserid → resources}/static/jquery/controller/controller.html
  261. 0  {browserid → resources}/static/jquery/controller/controller.js
  262. 0  {browserid → resources}/static/jquery/controller/controller_test.js
  263. 0  {browserid → resources}/static/jquery/controller/history/history.html
  264. 0  {browserid → resources}/static/jquery/controller/history/history.js
  265. 0  {browserid → resources}/static/jquery/controller/history/html5/html5.js
  266. 0  {browserid → resources}/static/jquery/controller/history/html5/qunit.html
  267. 0  {browserid → resources}/static/jquery/controller/history/html5/qunit/qunit.js
  268. 0  {browserid → resources}/static/jquery/controller/history/qunit.html
  269. 0  {browserid → resources}/static/jquery/controller/history/qunit/qunit.js
  270. 0  {browserid → resources}/static/jquery/controller/pages/document.js
  271. 0  {browserid → resources}/static/jquery/controller/pages/listening.js
  272. 0  {browserid → resources}/static/jquery/controller/pages/plugin.js
  273. 0  {browserid → resources}/static/jquery/controller/qunit.html
  274. 0  {browserid → resources}/static/jquery/controller/subscribe/funcunit.html
  275. 0  {browserid → resources}/static/jquery/controller/subscribe/subscribe.html
  276. 0  {browserid → resources}/static/jquery/controller/subscribe/subscribe.js
  277. 0  {browserid → resources}/static/jquery/controller/view/qunit.html
  278. 0  {browserid → resources}/static/jquery/controller/view/test/qunit/controller_view_test.js
  279. 0  {browserid → resources}/static/jquery/controller/view/test/qunit/qunit.js
  280. 0  {browserid → resources}/static/jquery/controller/view/test/qunit/views/init.micro
  281. 0  {browserid → resources}/static/jquery/controller/view/view.js
  282. 0  {browserid → resources}/static/jquery/dom/closest/closest.js
  283. 0  {browserid → resources}/static/jquery/dom/compare/compare.html
  284. 0  {browserid → resources}/static/jquery/dom/compare/compare.js
  285. 0  {browserid → resources}/static/jquery/dom/compare/compare_test.js
  286. 0  {browserid → resources}/static/jquery/dom/compare/qunit.html
  287. 0  {browserid → resources}/static/jquery/dom/cookie/cookie.js
  288. 0  {browserid → resources}/static/jquery/dom/cur_styles/cur_styles.html
  289. 0  {browserid → resources}/static/jquery/dom/cur_styles/cur_styles.js
  290. 0  {browserid → resources}/static/jquery/dom/cur_styles/cur_styles_test.js
  291. 0  {browserid → resources}/static/jquery/dom/cur_styles/qunit.html
  292. 0  ...static/jquery/dom/dimensions/test/qunit → resources/static/jquery/dom/cur_styles/test}/curStyles.micro
  293. 0  {browserid → resources}/static/jquery/dom/dimensions/dimensions.html
  294. 0  {browserid → resources}/static/jquery/dom/dimensions/dimensions.js
  295. 0  {browserid → resources}/static/jquery/dom/dimensions/qunit.html
  296. 0  ...static/jquery/dom/cur_styles/test → resources/static/jquery/dom/dimensions/test/qunit}/curStyles.micro
  297. 0  {browserid → resources}/static/jquery/dom/dimensions/test/qunit/dimensions_test.js
  298. 0  {browserid → resources}/static/jquery/dom/dimensions/test/qunit/outer.micro
  299. 0  {browserid → resources}/static/jquery/dom/dimensions/test/qunit/qunit.js
  300. 0  {browserid → resources}/static/jquery/dom/dom.js
Sorry, we could not display the entire diff because too many files (1,193) changed.
View
1  .gitignore
@@ -4,4 +4,5 @@
/node_modules
/var
/rpmbuild
+/npm-debug.log
View
23 ChangeLog
@@ -1,3 +1,26 @@
+train-2011.11.03:
+ * Remember the last used email for a site, and optimize the default selection based on this: #1
+ * Fix regression where verification of assertions would fail for https sites: #500 (also hot-fixed in production https://github.com/mozilla/browserid/commit/1528364)
+ * improved end user visible error messages: #448, #465, #512, #515
+ * style/transition improvements for desktop and mobile devices: #494, #502, #522, #527
+ * refuse to send out more than one email per minute to the same address: #430
+ * be *really* smart about how long to display tool-tips in the dialog: #508
+ * behave reasonably (at least display content) when javascript is disabled: #510
+ * remember the users email as they transition between screens, when appropriate: #476
+ * Suppress iOS autocapitalizion and auto-correction for email addresses: #464
+ * Improve front end email address validation: #513
+ * Improve repository organization: #503 & #488
+ * As part of above and in prep for #460 - all processes (browserid, verifier, etc) are now always run separately (never combined into the same express instance
+ * Test improvements: #520, #530, #531
+ * Fix undefined reference (crash) in verifier after verification failure: #523 (hot-fixed in production: https://github.com/mozilla/browserid/commit/ba3c53)
+ * Remove UI that corresponds to unimplemented features: #519
+ * Handle upper case letters in domain part of email addresses properly: #501
+ * Use a more conventional log format that includes time-stamps when logging to file. closes #234
+ * Shutdown gracefully whenever possible, and always log why we go down: #529
+ * 'LOG_TO_CONSOLE' env var for verbose console output during tests: #530
+ * more checks around '/code_update' URL invocation - for bug #699171
+ * Many minor bug-fixes: #497, #532
+
train-2011.10.27:
* link fixing ('need help?' to point to SUMO): #378
* unit tests repaired: #469 (broken in fix to #82)
View
378 bin/browserid
@@ -0,0 +1,378 @@
+#!/usr/bin/env node
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const
+fs = require('fs'),
+path = require('path'),
+url = require('url'),
+http = require('http');
+sessions = require('connect-cookie-session'),
+urlparse = require('urlparse');
+
+// add lib/ to the require path
+require.paths.unshift(path.join(__dirname, '..', 'lib'));
+
+const
+wsapi = require('browserid/wsapi.js'),
+ca = require('browserid/ca.js'),
+httputils = require('httputils.js'),
+express = require('express'),
+secrets = require('secrets.js'),
+db = require('db.js'),
+config = require('configuration.js'),
+heartbeat = require('heartbeat.js'),
+metrics = require("metrics.js"),
+logger = require("logging.js").logger
+forward = require('browserid/http_forward'),
+shutdown = require('shutdown');
+
+var app = undefined;
+
+app = express.createServer();
+
+logger.info("browserid server starting up");
+
+const COOKIE_SECRET = secrets.hydrateSecret('browserid_cookie', config.get('var_path'));
+const COOKIE_KEY = 'browserid_state';
+
+// verify that we have a keysigner configured
+if (!config.get('keysigner_url')) {
+ logger.error('missing required configuration - url for the keysigner (KEYSIGNER_URL in env)');
+ process.exit(1);
+}
+
+function internal_redirector(new_url, suppress_noframes) {
+ return function(req, resp, next) {
+ if (suppress_noframes)
+ resp.removeHeader('x-frame-options');
+ req.url = new_url;
+ return next();
+ };
+}
+
+function router(app) {
+ app.set("views", path.join(__dirname, "..", "resources", "views"));
+
+ app.set('view options', {
+ production: config.get('use_minified_resources')
+ });
+
+ // this should probably be an internal redirect
+ // as soon as relative paths are figured out.
+ app.get('/sign_in', function(req, res, next ) {
+ metrics.userEntry(req);
+ res.render('dialog.ejs', {
+ title: 'A Better Way to Sign In',
+ layout: 'dialog_layout.ejs',
+ useJavascript: true,
+ production: config.get('use_minified_resources')
+ });
+ });
+
+ app.get("/unsupported_dialog", function(req,res) {
+ res.render('unsupported_dialog.ejs', {layout: 'dialog_layout.ejs', useJavascript: false});
+ });
+
+ // simple redirects (internal for now)
+ app.get('/register_iframe', internal_redirector('/dialog/register_iframe.html',true));
+
+ // Used for a relay page for communication.
+ app.get("/relay", function(req,res, next) {
+ // Allow the relay to be run within a frame
+ res.removeHeader('x-frame-options');
+ res.render('relay.ejs', {
+ layout: false,
+ production: config.get('use_minified_resources')
+ });
+ });
+
+
+ app.get('/', function(req,res) {
+ res.render('index.ejs', {title: 'A Better Way to Sign In', fullpage: true});
+ });
+
+ app.get("/signup", function(req, res) {
+ res.render('signup.ejs', {title: 'Sign Up', fullpage: false});
+ });
+
+ app.get("/forgot", function(req, res) {
+ res.render('forgot.ejs', {title: 'Forgot Password', fullpage: false, email: req.query.email});
+ });
+
+ app.get("/signin", function(req, res) {
+ res.render('signin.ejs', {title: 'Sign In', fullpage: false});
+ });
+
+ app.get("/about", function(req, res) {
+ res.render('about.ejs', {title: 'About', fullpage: false});
+ });
+
+ app.get("/tos", function(req, res) {
+ res.render('tos.ejs', {title: 'Terms of Service', fullpage: false});
+ });
+
+ app.get("/privacy", function(req, res) {
+ res.render('privacy.ejs', {title: 'Privacy Policy', fullpage: false});
+ });
+
+ app.get("/verify_email_address", function(req, res) {
+ res.render('verifyuser.ejs', {title: 'Complete Registration', fullpage: true, token: req.query.token});
+ });
+
+ app.get("/add_email_address", function(req,res) {
+ res.render('verifyemail.ejs', {title: 'Verify Email Address', fullpage: false});
+ });
+
+ // REDIRECTS
+ REDIRECTS = {
+ "/manage": "/",
+ "/users": "/",
+ "/users/": "/",
+ "/primaries" : "/developers",
+ "/primaries/" : "/developers",
+ "/developers" : "https://github.com/mozilla/browserid/wiki/How-to-Use-BrowserID-on-Your-Site"
+ };
+
+ // set up all the redirects
+ // oh my watch out for scope issues on var url - closure time
+ for (var url in REDIRECTS) {
+ (function(from,to) {
+ app.get(from, function(req, res) {
+ res.redirect(to);
+ });
+ })(url, REDIRECTS[url]);
+ }
+
+ // register all the WSAPI handlers
+ wsapi.setup(app);
+
+ // setup health check / heartbeat
+ heartbeat.setup(app, function(cb) {
+ // let's check stuff! first the heartbeat of our keysigner
+ heartbeat.check(config.get('keysigner_url'), cb);
+ });
+
+ // the public key
+ app.get("/pk", function(req, res) {
+ res.json(ca.PUBLIC_KEY.toSimpleObject());
+ });
+
+ // vep bundle of JavaScript
+ app.get("/vepbundle", function(req, res) {
+ fs.readFile(__dirname + "/../node_modules/jwcrypto/vepbundle.js", function(error, content) {
+ if (error) {
+ res.writeHead(500);
+ res.end("oops");
+ console.log(error);
+ } else {
+ res.writeHead(200, {'Content-Type': 'text/javascript'});
+ res.write(content);
+ res.end();
+ }
+ });
+ });
+
+ shutdown.installUpdateHandler(app, function(readyForShutdown) {
+ logger.debug("closing database connection");
+ db.close(readyForShutdown)
+ });
+};
+
+// request to logger, dev formatted which omits personal data in the requests
+app.use(express.logger({
+ format: config.get('express_log_format'),
+ stream: {
+ write: function(x) {
+ logger.info(typeof x === 'string' ? x.trim() : x);
+ }
+ }
+}));
+
+// if these are verify requests, we'll redirect them off
+// to the verifier
+if (config.get('verifier_url')) {
+ app.use(function(req, res, next) {
+ if (/^\/verify$/.test(req.url)) {
+ forward(
+ config.get('verifier_url'), req, res,
+ function(err) {
+ if (err) {
+ logger.error("error forwarding request:", err);
+ }
+ });
+ } else {
+ return next();
+ }
+ });
+}
+
+// over SSL?
+var overSSL = (config.get('scheme') == 'https');
+
+app.use(express.cookieParser());
+
+var cookieSessionMiddleware = sessions({
+ secret: COOKIE_SECRET,
+ key: COOKIE_KEY,
+ cookie: {
+ path: '/wsapi',
+ httpOnly: true,
+ // IMPORTANT: we allow users to go 1 weeks on the same device
+ // without entering their password again
+ maxAge: config.get('authentication_duration_ms'),
+ secure: overSSL
+ }
+});
+
+// cookie sessions && cache control
+app.use(function(req, resp, next) {
+ // cookie sessions are only applied to calls to /wsapi
+ // as all other resources can be aggressively cached
+ // by layers higher up based on cache control headers.
+ // the fallout is that all code that interacts with sessions
+ // should be under /wsapi
+ if (/^\/wsapi/.test(req.url)) {
+ // explicitly disallow caching on all /wsapi calls (issue #294)
+ resp.setHeader('Cache-Control', 'no-cache, max-age=0');
+
+ // we set this parameter so the connect-cookie-session
+ // sends the cookie even though the local connection is HTTP
+ // (the load balancer does SSL)
+ if (overSSL)
+ req.connection.proxySecure = true;
+
+ return cookieSessionMiddleware(req, resp, next);
+
+ } else {
+ return next();
+ }
+});
+
+config.performSubstitution(app);
+
+// verify all JSON responses are objects - prevents regression on issue #217
+app.use(function(req, resp, next) {
+ var realRespJSON = resp.json;
+ resp.json = function(obj) {
+ if (!obj || typeof obj !== 'object') {
+ logger.error("INTERNAL ERROR! *all* json responses must be objects");
+ throw "internal error";
+ }
+ realRespJSON.call(resp, obj);
+ };
+ return next();
+});
+
+app.use(express.bodyParser());
+
+// Check CSRF token early. POST requests are only allowed to
+// /wsapi and they always must have a valid csrf token
+app.use(function(req, resp, next) {
+ // only on POSTs
+ if (req.method == "POST") {
+ var denied = false;
+ if (!/^\/wsapi/.test(req.url)) { // post requests only allowed to /wsapi
+ denied = true;
+ logger.warn("CSRF validation failure: POST only allowed to /wsapi urls. not '" + req.url + "'");
+ }
+
+ else if (req.session === undefined) { // there must be a session
+ denied = true;
+ logger.warn("CSRF validation failure: POST calls to /wsapi require an active session");
+ }
+
+ // the session must have a csrf token
+ else if (typeof req.session.csrf !== 'string') {
+ denied = true;
+ logger.warn("CSRF validation failure: POST calls to /wsapi require an csrf token to be set");
+ }
+
+ // and the token must match what is sent in the post body
+ else if (req.body.csrf != req.session.csrf) {
+ denied = true;
+ // if any of these things are false, then we'll block the request
+ logger.warn("CSRF validation failure, token mismatch. got:" + req.body.csrf + " want:" + req.session.csrf);
+ }
+
+ if (denied) return httputils.badRequest(resp, "CSRF violation");
+
+ }
+ return next();
+});
+
+// a tweak to get the content type of host-meta correct
+app.use(function(req, resp, next) {
+ if (req.url === '/.well-known/host-meta') {
+ resp.setHeader('content-type', 'text/xml');
+ }
+ next();
+});
+
+// Strict Transport Security
+app.use(function(req, resp, next) {
+ if (overSSL) {
+ // expires in 30 days, include subdomains like www
+ resp.setHeader("Strict-Transport-Security", "max-age=2592000; includeSubdomains");
+ }
+ next();
+});
+
+// prevent framing
+app.use(function(req, resp, next) {
+ resp.setHeader('x-frame-options', 'DENY');
+ next();
+});
+
+// add the actual URL handlers other than static
+router(app);
+
+// use the express 'static' middleware for serving of static files (cache headers, HTTP range, etc)
+app.use(express.static(path.join(__dirname, "..", "resources", "static")));
+
+// open the databse
+db.open(config.get('database'), function () {
+
+ // shut down express gracefully on SIGINT
+ shutdown.handleTerminationSignals(app, function(readyForShutdown) {
+ db.close(readyForShutdown)
+ });
+
+ var bindTo = config.get('bind_to');
+ app.listen(bindTo.port, bindTo.host, function() {
+ logger.info("running on http://" + app.address().address + ":" + app.address().port);
+ });
+});
View
70 verifier/run.js → bin/keysigner
@@ -35,19 +35,69 @@
*
* ***** END LICENSE BLOCK ***** */
-var sys = require("sys"),
- path = require("path"),
- fs = require("fs"),
- express = require("express");
+// I sign keys. That's what I do.
-var PRIMARY_HOST = "127.0.0.1";
-var PRIMARY_PORT = 62800;
+const
+path = require('path'),
+express = require('express');
-var handler = require("./app.js");
+// add lib/ to the require path for our own libs
+require.paths.unshift(path.join(__dirname, '..', 'lib'));
+const
+config = require('configuration.js'),
+validate = require('validate.js'),
+metrics = require("metrics.js"),
+logger = require("logging.js").logger,
+ca = require('keysigner/ca.js'),
+heartbeat = require('heartbeat'),
+shutdown = require('shutdown');
+
+// create an express server
var app = express.createServer();
-// let the specific server interact directly with the express server to register their middleware
-if (handler.setup) handler.setup(app);
+// our server will log
+app.use(express.logger({
+ format: config.get('express_log_format'),
+ stream: {
+ write: function(x) {
+ logger.info(typeof x === 'string' ? x.trim() : x);
+ }
+ }
+}));
+
+app.use(function(req, resp, next) {
+ next();
+});
+
+// parse POST bodies
+app.use(express.bodyParser());
+
+heartbeat.setup(app);
+
+// and our single function
+app.post('/wsapi/cert_key', validate(["email", "pubkey"]), function(req, resp) {
+ // parse the pubkey
+ var pk = ca.parsePublicKey(req.body.pubkey);
+
+ // same account, we certify the key
+ // we certify it for a day for now
+ var expiration = new Date();
+ expiration.setTime(new Date().valueOf() + config.get('certificate_validity_ms'));
+ var cert = ca.certify(req.body.email, pk, expiration);
+
+ resp.writeHead(200, {'Content-Type': 'text/plain'});
+ resp.write(cert);
+ resp.end();
+});
+
+// shutdown when code_update is invoked
+shutdown.installUpdateHandler(app);
+
+// shutdown nicely on signals
+shutdown.handleTerminationSignals(app);
-app.listen(PRIMARY_PORT, PRIMARY_HOST);
+var bindTo = config.get('bind_to');
+app.listen(bindTo.port, bindTo.host, function() {
+ logger.info("running on http://" + app.address().address + ":" + app.address().port);
+});
View
15 performance/run.js → bin/load_gen
@@ -20,7 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
- * Lloyd Hilaiel <lloyd@hilaiel.com>
+ * Lloyd Hilaiel <lloyd@hilaiel.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -40,8 +40,7 @@
* tool, which is capable of analysing the maximum active users that
* a browserid deployment can support */
-
-// option processing with optimist
+// option processing with optimist
var argv = require('optimist')
.usage('Apply load to a BrowserID server.\nUsage: $0', [ "foo" ])
.alias('h', 'help')
@@ -92,14 +91,14 @@ var completed = {
const activitiesPerUserPerSecond = (40.0 / ( 24 * 60 * 60 ));
// activities
-var activity = {
+var activity = {
"signup": {
// a %20 montly growth rate means there's a 20% probability of
// the monthly activity generated by an active user being a
// new user signup
probability: (1.0 / (40 * 28 * .2))
},
- "reset_pass": {
+ "reset_pass": {
// users forget their password once every 4 weeks
probability: (1.0 / (40 * 28.0))
},
@@ -129,7 +128,7 @@ var activity = {
// now attach "start functions" to the activity map by including
// the implementation of each activity
Object.keys(activity).forEach(function(k) {
- activity[k].startFunc = require("./lib/" + k).startFunc;
+ activity[k].startFunc = require("../lib/performance/" + k).startFunc;
});
// probs is a 2d array mapping normalized probabilities from 0-1 to
@@ -237,12 +236,12 @@ function poll() {
// how many active users would we like to simulate
var targetActive = args.m;
-
+
// if we're not throttled, then we'll trying 150% as many as
// we're simulating right now. If we're not simulating at least
// 10000 active users, that shall be our lower bound
if (!targetActive) {
- if (averages[0] > 10000) targetActive = averages[0] * 1.5;
+ if (averages[0] > 10000) targetActive = averages[0] * 1.5;
else targetActive = 10000;
}
View
87 verifier/app.js → bin/verifier 100644 → 100755
@@ -1,3 +1,5 @@
+#!/usr/bin/env node
+
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@@ -34,26 +36,46 @@
*
* ***** END LICENSE BLOCK ***** */
-const path = require('path'),
- url = require('url'),
- fs = require('fs'),
-certassertion = require('./lib/certassertion.js'),
- express = require('express'),
- metrics = require('../libs/metrics.js'),
- heartbeat = require('../libs/heartbeat.js'),
- logger = require('../libs/logging.js').logger;
+const
+sys = require("sys"),
+path = require('path'),
+url = require('url'),
+fs = require('fs'),
+express = require('express'),
+certassertion = require('../lib/verifier/certassertion.js'),
+metrics = require('../lib/metrics'),
+heartbeat = require('../lib/heartbeat'),
+logger = require('../lib/logging').logger,
+config = require('../lib/configuration'),
+shutdown = require('../lib/shutdown');
logger.info("verifier server starting up");
-// updating this call for certs now (Ben - 2011-09-06)
-// assertion is the single assertion of email
-// audience is the intended audience
-// certificates is the list of chained certificates, CSV-style
-function doVerify(req, resp, next) {
+var app = express.createServer();
+
+// request to logger, dev formatted which omits personal data in the requests
+app.use(express.logger({
+ format: config.get('express_log_format'),
+ stream: {
+ write: function(x) {
+ logger.info(typeof x === 'string' ? x.trim() : x);
+ }
+ }
+}));
+
+app.use(express.bodyParser());
+
+// shutdown when /code_update is invoked
+shutdown.installUpdateHandler(app);
+
+// setup health check / heartbeat
+heartbeat.setup(app);
+
+app.post('/verify', function(req, resp, next) {
req.body = req.body || {}
- var assertion = (req.query && req.query.assertion) ? req.query.assertion : req.body.assertion;
- var audience = (req.query && req.query.audience) ? req.query.audience : req.body.audience;
+ var assertion = req.body.assertion;
+ var audience = req.body.audience;
if (!(assertion && audience))
return resp.json({ status: "failure", reason: "need assertion and audience" });
@@ -91,33 +113,12 @@ function doVerify(req, resp, next) {
});
});
-}
-
-exports.setup = function(app) {
- // request to logger, dev formatted which omits personal data in the requests
-
- app.use(express.logger({
- format: 'dev',
- stream: {
- write: function(x) {
- logger.info(typeof x === 'string' ? x.trim() : x);
- }
- }
- }));
-
- app.use(express.bodyParser());
-
- // code_update is an internal api that causes the node server to
- // shut down. This should never be externally accessible and
- // is used during the dead simple deployment procedure.
- app.get("/code_update", function (req, resp) {
- logger.warn("code updated. shutting down.");
- process.exit();
- });
+});
- // setup health check / heartbeat
- heartbeat.setup(app);
+// shutdown nicely on signals
+shutdown.handleTerminationSignals(app);
- app.post('/', doVerify);
- app.post('/verify', doVerify);
-};
+var bindTo = config.get('bind_to');
+app.listen(bindTo.port, bindTo.host, function(conn) {
+ logger.info("running on http://" + app.address().address + ":" + app.address().port);
+});
View
330 browserid/app.js
@@ -1,330 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla BrowserID.
- *
- * The Initial Developer of the Original Code is Mozilla.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-const
-fs = require('fs'),
-path = require('path'),
-url = require('url'),
-wsapi = require('./lib/wsapi.js'),
-ca = require('./lib/ca.js'),
-httputils = require('./lib/httputils.js'),
-sessions = require('connect-cookie-session'),
-express = require('express'),
-secrets = require('../libs/secrets.js'),
-db = require('./lib/db.js'),
-configuration = require('../libs/configuration.js'),
-heartbeat = require('../libs/heartbeat.js'),
-substitution = require('../libs/substitute.js');
-metrics = require("../libs/metrics.js"),
-logger = require("../libs/logging.js").logger;
-
-logger.info("browserid server starting up");
-
-// open the databse
-db.open(configuration.get('database'));
-
-const COOKIE_SECRET = secrets.hydrateSecret('browserid_cookie', configuration.get('var_path'));
-const COOKIE_KEY = 'browserid_state';
-
-function internal_redirector(new_url, suppress_noframes) {
- return function(req, resp, next) {
- if (suppress_noframes)
- resp.removeHeader('x-frame-options');
- req.url = new_url;
- return next();
- };
-}
-
-function router(app) {
- app.set("views", __dirname + '/views');
-
- app.set('view options', {
- production: configuration.get('use_minified_resources')
- });
-
- // this should probably be an internal redirect
- // as soon as relative paths are figured out.
- app.get('/sign_in', function(req, res, next ) {
- metrics.userEntry(req);
- res.render('dialog.ejs', {
- title: 'A Better Way to Sign In',
- layout: 'dialog_layout.ejs',
- useJavascript: true,
- production: configuration.get('use_minified_resources')
- });
- });
-
- app.get("/unsupported_dialog", function(req,res) {
- res.render('unsupported_dialog.ejs', {layout: 'dialog_layout.ejs', useJavascript: false});
- });
-
- // simple redirects (internal for now)
- app.get('/register_iframe', internal_redirector('/dialog/register_iframe.html',true));
-
- // Used for a relay page for communication.
- app.get("/relay", function(req,res, next) {
- // Allow the relay to be run within a frame
- res.removeHeader('x-frame-options');
- res.render('relay.ejs', {
- layout: false,
- production: configuration.get('use_minified_resources')
- });
- });
-
-
- app.get('/', function(req,res) {
- res.render('index.ejs', {title: 'A Better Way to Sign In', fullpage: true});
- });
-
- app.get("/signup", function(req, res) {
- res.render('signup.ejs', {title: 'Sign Up', fullpage: false});
- });
-
- app.get("/forgot", function(req, res) {
- res.render('forgot.ejs', {title: 'Forgot Password', fullpage: false, email: req.query.email});
- });
-
- app.get("/signin", function(req, res) {
- res.render('signin.ejs', {title: 'Sign In', fullpage: false});
- });
-
- app.get("/about", function(req, res) {
- res.render('about.ejs', {title: 'About', fullpage: false});
- });
-
- app.get("/tos", function(req, res) {
- res.render('tos.ejs', {title: 'Terms of Service', fullpage: false});
- });
-
- app.get("/privacy", function(req, res) {
- res.render('privacy.ejs', {title: 'Privacy Policy', fullpage: false});
- });
-
- app.get("/verify_email_address", function(req, res) {
- res.render('verifyuser.ejs', {title: 'Complete Registration', fullpage: true, token: req.query.token});
- });
-
- app.get("/add_email_address", function(req,res) {
- res.render('verifyemail.ejs', {title: 'Verify Email Address', fullpage: false});
- });
-
- // REDIRECTS
- REDIRECTS = {
- "/manage": "/",
- "/users": "/",
- "/users/": "/",
- "/primaries" : "/developers",
- "/primaries/" : "/developers",
- "/developers" : "https://github.com/mozilla/browserid/wiki/How-to-Use-BrowserID-on-Your-Site"
- };
-
- // set up all the redirects
- // oh my watch out for scope issues on var url - closure time
- for (var url in REDIRECTS) {
- (function(from,to) {
- app.get(from, function(req, res) {
- res.redirect(to);
- });
- })(url, REDIRECTS[url]);
- }
-
- // register all the WSAPI handlers
- wsapi.setup(app);
-
- // setup health check / heartbeat
- heartbeat.setup(app);
-
- // the public key
- app.get("/pk", function(req, res) {
- res.json(ca.PUBLIC_KEY.toSimpleObject());
- });
-
- // vep bundle of JavaScript
- app.get("/vepbundle", function(req, res) {
- fs.readFile(__dirname + "/../node_modules/jwcrypto/vepbundle.js", function(error, content) {
- if (error) {
- res.writeHead(500);
- res.end("oops");
- console.log(error);
- } else {
- res.writeHead(200, {'Content-Type': 'text/javascript'});
- res.write(content);
- res.end();
- }
- });
- });
-
- app.get('/code_update', function(req, resp, next) {
- logger.warn("code updated. shutting down.");
- process.exit();
- });
-};
-
-exports.setup = function(server) {
- // request to logger, dev formatted which omits personal data in the requests
- server.use(express.logger({
- format: 'dev',
- stream: {
- write: function(x) {
- logger.info(typeof x === 'string' ? x.trim() : x);
- }
- }
- }));
-
- // over SSL?
- var overSSL = (configuration.get('scheme') == 'https');
-
- server.use(express.cookieParser());
-
- var cookieSessionMiddleware = sessions({
- secret: COOKIE_SECRET,
- key: COOKIE_KEY,
- cookie: {
- path: '/wsapi',
- httpOnly: true,
- // IMPORTANT: we allow users to go 1 weeks on the same device
- // without entering their password again
- maxAge: configuration.get('authentication_duration_ms'),
- secure: overSSL
- }
- });
-
- // cookie sessions && cache control
- server.use(function(req, resp, next) {
- // cookie sessions are only applied to calls to /wsapi
- // as all other resources can be aggressively cached
- // by layers higher up based on cache control headers.
- // the fallout is that all code that interacts with sessions
- // should be under /wsapi
- if (/^\/wsapi/.test(req.url)) {
- // explicitly disallow caching on all /wsapi calls (issue #294)
- resp.setHeader('Cache-Control', 'no-cache, max-age=0');
-
- // we set this parameter so the connect-cookie-session
- // sends the cookie even though the local connection is HTTP
- // (the load balancer does SSL)
- if (overSSL)
- req.connection.proxySecure = true;
-
- return cookieSessionMiddleware(req, resp, next);
-
- } else {
- return next();
- }
- });
-
- // verify all JSON responses are objects - prevents regression on issue #217
- server.use(function(req, resp, next) {
- var realRespJSON = resp.json;
- resp.json = function(obj) {
- if (!obj || typeof obj !== 'object') {
- logger.error("INTERNAL ERROR! *all* json responses must be objects");
- throw "internal error";
- }
- realRespJSON.call(resp, obj);
- };
- return next();
- });
-
- server.use(express.bodyParser());
-
- // Check CSRF token early. POST requests are only allowed to
- // /wsapi and they always must have a valid csrf token
- server.use(function(req, resp, next) {
- // only on POSTs
- if (req.method == "POST") {
- var denied = false;
- if (!/^\/wsapi/.test(req.url)) { // post requests only allowed to /wsapi
- denied = true;
- logger.warn("CSRF validation failure: POST only allowed to /wsapi urls. not '" + req.url + "'");
- }
-
- if (req.session === undefined) { // there must be a session
- denied = true;
- logger.warn("CSRF validation failure: POST calls to /wsapi require an active session");
- }
-
- // the session must have a csrf token
- if (typeof req.session.csrf !== 'string') {
- denied = true;
- logger.warn("CSRF validation failure: POST calls to /wsapi require an csrf token to be set");
- }
-
- // and the token must match what is sent in the post body
- if (req.body.csrf != req.session.csrf) {
- denied = true;
- // if any of these things are false, then we'll block the request
- logger.warn("CSRF validation failure, token mismatch. got:" + req.body.csrf + " want:" + req.session.csrf);
- }
-
- if (denied) return httputils.badRequest(resp, "CSRF violation");
-
- }
- return next();
- });
-
- // a tweak to get the content type of host-meta correct
- server.use(function(req, resp, next) {
- if (req.url === '/.well-known/host-meta') {
- resp.setHeader('content-type', 'text/xml');
- }
- next();
- });
-
- // Strict Transport Security
- server.use(function(req, resp, next) {
- if (overSSL) {
- // expires in 30 days, include subdomains like www
- resp.setHeader("Strict-Transport-Security", "max-age=2592000; includeSubdomains");
- }
- next();
- });
-
- // prevent framing
- server.use(function(req, resp, next) {
- resp.setHeader('x-frame-options', 'DENY');
- next();
- });
-
- // add middleware to re-write urls if needed
- configuration.performSubstitution(server);
-
- // add the actual URL handlers other than static
- router(server);
-}
-
-exports.shutdown = function() {
- db.close();
-};
View
117 browserid/static/dialog/resources/error-messages.js
@@ -1,117 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla BrowserID.
- *
- * The Initial Developer of the Original Code is Mozilla.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-BrowserID.Errors = (function(){
- "use strict";
-
- var Errors = {
- authenticate: {
- type: "serverError",
- title: "Error Authenticating",
- message: "There was a technical problem while trying to log you in. Yucky!"
- },
-
- addEmail: {
- type: "serverError",
- title: "Error Adding Address",
- message: "There was a technical problem while trying to add this email to your account. Yucky!"
- },
-
- checkAuthentication: {
- type: "serverError",
- title: "Error Checking Authentication",
- message: "There was a technical problem while trying to log you in. Yucky!"
- },
-
- createUser: {
- type: "serverError",
- title: "Error Creating Account",
- message: "There was a technical problem while trying to create your account. Yucky!"
- },
-
- getAssertion: {
- type: "serverError",
- title: "Error Getting Assertion",
- message: "There was a technical problem while trying to authenticate you. Yucky!"
- },
-
- isEmailRegistered: {
- type: "serverError",
- title: "Error Checking Email Address",
- message: "There was a technical problem while trying to check that email address. Yucky!"
- },
-
- logoutUser: {
- type: "serverError",
- title: "Logout Failed",
- message: "An error was encountered while signing you out. Yucky!"
- },
-
- offline: {
- type: "networkError",
- title: "You are offline!",
- message: "Unfortunately, BrowserID cannot communicate while offline!"
- },
-
- registration: {
- type: "serverError",
- title: "Registration Failed",
- message: "An error was encountered and the signup cannot be completed. Yucky!"
- },
-
- requestPasswordReset: {
- type: "serverError",
- title: "Error Resetting Password",
- message: "There was a technical problem while trying to reset your password."
- },
-
- signIn: {
- type: "serverError",
- title: "Signin Failed",
- message: "There was an error signing in. Yucky!"
- },
-
- syncAddress: {
- type: "serverError",
- title: "Error Syncing Address",
- message: "There was a technical problem while trying to synchronize your account. Yucky!"
- }
-
- };
-
-
- return Errors;
-}());
-
-
View
54 browserid/static/dialog/views/authenticate.ejs
@@ -1,54 +0,0 @@
- <strong>Sign in using</strong>
- <ul class="inputs">
-
- <li>
- <label for="email" class="serif">Email</label>
- <input id="email" class="sans" type="email" autocapitalize="off" autocorrect="off" value="<%= email %>" />
-
- <div id="email_format" class="tooltip" for="email">
- This field must be an email address.
- </div>
-
- <div id="email_required" class="tooltip" for="email">
- The email field is required.
- </div>
- </li>
-
- <li id="hint_section" class="start">
- <p>Enter your email address to sign in to <strong><%= sitename %></strong></p>
- </li>
-
- <li id="create_text_section" class="newuser">
- <p><strong>Welcome to BrowserID!</strong></p>
- <p>This email looks new, so let's get you set up.</p>
- </li>
-
- <li id="password_section" class="returning">
-
- <label for="password" class="half serif">Password</label>
- <div class="half right">
- <a id="forgotPassword" href="#">forgot your password?</a>
- </div>
- <input id="password" class="sans" type="password" maxlength="80">
-
-
- <div id="password_required" class="tooltip" for="password">
- The password field is required.
- </div>
-
- <div id="cannot_authenticate" class="tooltip" for="password">
- The account cannot be logged in with this username and password.
- </div>
- </li>
-
- </ul>
-
- <div class="submit cf">
- <button class="start">next</button>
- <button class="newuser">Verify Email</button>
-
- <button class="returning">sign in</button>
-
- <button class="forgot">Reset Password</button>
- <button id="cancel_forgot_password" class="forgot">Cancel</button>
- </div>
View
134 browserid/tests/lib/start-stop.js
@@ -1,134 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla BrowserID.
- *
- * The Initial Developer of the Original Code is Mozilla.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-const assert = require('assert'),
- fs = require('fs'),
- path = require('path'),
- wsapi = require('./wsapi.js');
-
-const varPath = path.join(path.dirname(path.dirname(__dirname)), "var");
-
-function removeVarDir() {
- try {
- fs.readdirSync(varPath).forEach(function(f) {
- fs.unlinkSync(path.join(varPath, f));
- });
- fs.rmdirSync(varPath);
- } catch(e) {}
-}
-
-exports.addStartupBatches = function(suite) {
- suite.addBatch({
- "remove the user database": {
- topic: function() {
- removeVarDir();
- fs.mkdirSync(varPath, 0755);
- return true;
- },
- "directory should exist": function(x) {
- assert.ok(fs.statSync(varPath).isDirectory());
- }
- }
- });
-
- suite.addBatch({
- "run the server": {
- topic: function() {
- const server = require("../../run.js");
- server.runServer();
- return true;
- },
- "server should be running": {
- topic: wsapi.get('/__heartbeat__'),
- "server is running": function (r, err) {
- assert.equal(r.code, 200);
- }
- }
- }
- });
-
- suite.addBatch({
- "wait for readiness": {
- topic: function() {
- var cb = this.callback;
- require("../../lib/db.js").onReady(function() { cb(true) });
- },
- "readiness has arrived": function(v) {
- assert.ok(v);
- }
- }
- });
-};
-
-exports.addShutdownBatches = function(suite) {
- // stop the server
- suite.addBatch({
- "stop the server": {
- topic: function() {
- const server = require("../../run.js");
- var cb = this.callback;
- server.stopServer(function() { cb(true); });
- },
- "stopped": function(x) {
- assert.strictEqual(x, true);
- }
- }
- });
-
- // stop the database
- suite.addBatch({
- "stop the database": {
- topic: function() {
- require("../../lib/db.js").close(this.callback);
- },
- "stopped": function(x) {
- assert.isUndefined(x);
- }
- }
- });
-
- // clean up
- suite.addBatch({
- "clean up": {
- topic: function() {
- removeVarDir();
- return true;
- },
- "directory should not exist": function(x) {
- assert.throws(function(){ fs.statSync(varPath) });
- }
- }
- });
-}
View
40 browserid/views/index.ejs
@@ -1,40 +0,0 @@
- <div id="content" style="display:none;">
- <div id="manage">
- <h1 class="serif">Account Manager</h1>
- <div class="edit cf">
- <strong>Your Email Addresses</strong>
-
- <a id="manageAccounts" href="#">edit</a>
- <a id="cancelManage" href="#">done</a>
- </div>
- <ul id="emailList">
- </ul>
- <div id="disclaimer">You may, at any time, <a href="#" id="cancelAccount">cancel your account</a></div>
- </div>
- </div>
-
- <div id="vAlign" style="display:none;">
- <div id="signUp">
- <div id="card"><img src="/i/slit.png"></div>
- <div id="hint"></div>
- <div id="status"></div>
-
- <p>Connect with <em>BrowserID</em>, the safest &amp; easiest way to sign in.</p>
- <p>
- <a class="granted info" href="/about">Take the tour</a> or
- <a href="/signup" class="button granted create">sign up</a>
- </p>
- </div>
- </div>
-
-<script type="text/html" id="templateUser">
- <li class="identity cf">
- <div class="email">{{ email }}</div>
- <div class="activity cf">
- <button class="delete">remove</button>
- <!-- removed registration info. We want to replace this with Last Used At ... -->
- <!-- <abbr title="Registered: {{ created }}" class="status">Registered {{ relative }}.</abbr>-->
- </div>
- </li>
-</script>
-
View
0  DEPLOYMENT.md → docs/DEPLOYMENT.md
File renamed without changes
View
0  performance/README.md → docs/LOAD_GENERATION.md
File renamed without changes
View
0  ORGANIZATION.md → docs/ORGANIZATION.md
File renamed without changes
View
0  rp/index.html → example/index.html
File renamed without changes
View
0  rp/jquery-min.js → example/jquery-min.js
File renamed without changes
View
11 browserid/lib/ca.js → lib/browserid/ca.js
@@ -39,8 +39,7 @@
var jwcert = require('jwcrypto/jwcert'),
jwk = require('jwcrypto/jwk'),
jws = require('jwcrypto/jws'),
- configuration = require('../../libs/configuration'),
- secrets = require('../../libs/secrets'),
+ configuration = require('configuration'),
path = require("path"),
fs = require("fs");
@@ -59,7 +58,7 @@ function parseCert(serializedCert) {
function certify(email, publicKey, expiration) {
if (expiration == null)
throw "expiration cannot be null";
- return new jwcert.JWCert(HOSTNAME, expiration, publicKey, {email: email}).sign(secrets.SECRET_KEY);
+ return new jwcert.JWCert(HOSTNAME, expiration, publicKey, {email: email}).sign(configuration.get('secret_key'));
}
function verifyChain(certChain, cb) {
@@ -70,8 +69,8 @@ function verifyChain(certChain, cb) {
// for now we only do browserid.org issued keys
if (issuer != HOSTNAME)
return next(null);
-
- next(secrets.PUBLIC_KEY);
+
+ next(exports.PUBLIC_KEY);
}, cb);
}
@@ -80,4 +79,4 @@ exports.certify = certify;
exports.verifyChain = verifyChain;
exports.parsePublicKey = parsePublicKey;
exports.parseCert = parseCert;
-exports.PUBLIC_KEY = secrets.PUBLIC_KEY;
+exports.PUBLIC_KEY = configuration.get('public_key');
View
9 browserid/lib/email.js → lib/browserid/email.js
@@ -34,13 +34,13 @@
* ***** END LICENSE BLOCK ***** */
const
-db = require('./db'),
+db = require('db.js'),
emailer = require('nodemailer'),
fs = require('fs'),
path = require('path'),
mustache = require('mustache'),
-config = require('../../libs/configuration.js'),
-logger = require('../../libs/logging.js').logger;
+config = require('configuration.js'),
+logger = require('logging.js').logger;
/* if smtp parameters are configured, use them */
var smtp_params = config.get('smtp');
@@ -48,10 +48,11 @@ if (smtp_params && smtp_params.host) {
emailer.SMTP = { host: smtp_params.host };
logger.info("delivering email via SMTP host: " + emailer.SMTP.host);
if (smtp_params.user) {
- logger.info("authenticating to email host as" + emailer.SMTP.user);
emailer.SMTP.use_authentication = true;
emailer.SMTP.user = smtp_params.user;
emailer.SMTP.pass = smtp_params.pass;
+
+ logger.info("authenticating to email host as " + emailer.SMTP.user);
}
}
View
0  browserid/lib/fake_verification.js → lib/browserid/fake_verification.js
File renamed without changes
View
49 lib/browserid/http_forward.js
@@ -0,0 +1,49 @@
+const
+url = require('url'),
+http = require('http'),
+https = require('https'),
+logger = require('logging.js').logger,
+querystring = require('querystring');
+
+module.exports = function(dest, req, res, cb) {
+ var u = url.parse(dest.toString());
+
+ var m = u.protocol === 'http:' ? http : https;
+
+ var preq = m.request({
+ host: u.hostname,
+ port: u.port,
+ path: u.pathname,
+ method: req.method
+ }, function(pres) {
+ res.writeHead(
+ pres.statusCode,
+ pres.headers
+ );
+ pres.on('data', function (chunk) {
+ res.write(chunk);
+ }).on('end', function() {
+ res.end();
+ cb();
+ });
+ }).on('error', function(e) {
+ res.end();
+ cb(e);
+ });
+
+ if (req.headers['content-type']) {
+ preq.setHeader('content-type', req.headers['content-type']);
+ }
+
+ // if the body has already been parsed, we'll write it
+ if (req.body) {
+ var data = querystring.stringify(req.body);
+ preq.setHeader('content-length', data.length);
+ preq.write(data);
+ preq.end();
+ } else {
+ req.on('data', function(chunk) { preq.write(chunk) })
+ .on('end', function() { preq.end() });
+ }
+ logger.info("forwarding request: " + req.url + " -> " + dest);
+};
View
0  browserid/lib/prove_template.txt → lib/browserid/prove_template.txt
File renamed without changes
View
164 browserid/lib/wsapi.js → lib/browserid/wsapi.js
@@ -39,38 +39,17 @@
// with HTTP methods and the like, apply middleware, etc.
const
-db = require('./db.js'),
+db = require('db.js'),
url = require('url'),
-httputils = require('./httputils.js'),
+httputils = require('httputils.js'),
email = require('./email.js'),
bcrypt = require('bcrypt'),
crypto = require('crypto'),
-logger = require('../../libs/logging.js').logger,
+logger = require('logging.js').logger,
ca = require('./ca.js'),
-configuration = require('../../libs/configuration.js');
-
-function checkParams(params) {
- return function(req, resp, next) {
- var params_in_request=null;
- if (req.method === "POST") {
- params_in_request = req.body;
- } else {
- params_in_request = req.query;
- }
-
- try {
- params.forEach(function(k) {
- if (!params_in_request.hasOwnProperty(k) || typeof params_in_request[k] !== 'string') {
- throw k;
- }
- });
- } catch(e) {
- logger.error(e.toString());
- return httputils.badRequest(resp, "missing '" + e + "' argument");
- }
- next();
- };
-}
+config = require('configuration.js'),
+validate = require('validate'),
+forward = require('browserid/http_forward');
// log a user out, clearing everything from their session except the csrf token
function clearAuthenticatedUser(session) {
@@ -79,7 +58,6 @@ function clearAuthenticatedUser(session) {
});
}
-
function setAuthenticatedUser(session, email) {
session.authenticatedUser = email;
session.authenticatedAt = new Date();
@@ -91,7 +69,7 @@ function isAuthed(req) {
if (req.session.authenticatedUser) {
if (!Date.parse(req.session.authenticatedAt) > 0) throw "bad timestamp";
if (new Date() - new Date(req.session.authenticatedAt) >
- configuration.get('authentication_duration_ms'))
+ config.get('authentication_duration_ms'))
{
throw "expired";
}
@@ -173,31 +151,39 @@ function setup(app) {
* user via their claimed email address. Upon timeout expiry OR clickthrough
* the staged user account transitions to a valid user account
*/
- app.post('/wsapi/stage_user', checkParams([ "email", "site" ]), function(req, resp) {
+ app.post('/wsapi/stage_user', validate([ "email", "site" ]), function(req, resp) {
// staging a user logs you out.
clearAuthenticatedUser(req.session);
- try {
- // upon success, stage_user returns a secret (that'll get baked into a url
- // and given to the user), on failure it throws
- db.stageUser(req.body.email, function(secret) {
- // store the email being registered in the session data
- if (!req.session) req.session = {};
+ db.lastStaged(req.body.email, function (last) {
+ if (last && (new Date() - last) < config.get('min_time_between_emails_ms')) {
+ logger.warn('throttling request to stage email address ' + req.body.email + ', only ' +
+ ((new Date() - last) / 1000.0) + "s elapsed");
+ return httputils.forbidden(resp, "throttling. try again later.");
+ }
- // store the secret we're sending via email in the users session, as checking
- // that it still exists in the database is the surest way to determine the
- // status of the email verification.
- req.session.pendingCreation = secret;
+ try {
+ // upon success, stage_user returns a secret (that'll get baked into a url
+ // and given to the user), on failure it throws
+ db.stageUser(req.body.email, function(secret) {
+ // store the email being registered in the session data
+ if (!req.session) req.session = {};
- resp.json({ success: true });
+ // store the secret we're sending via email in the users session, as checking
+ // that it still exists in the database is the surest way to determine the
+ // status of the email verification.
+ req.session.pendingCreation = secret;
- // let's now kick out a verification email!
- email.sendNewUserEmail(req.body.email, req.body.site, secret);
- });
- } catch(e) {
- // we should differentiate tween' 400 and 500 here.
- httputils.badRequest(resp, e.toString());
- }
+ resp.json({ success: true });
+
+ // let's now kick out a verification email!
+ email.sendNewUserEmail(req.body.email, req.body.site, secret);
+ });
+ } catch(e) {
+ // we should differentiate tween' 400 and 500 here.
+ httputils.badRequest(resp, e.toString());
+ }
+ });
});
app.get('/wsapi/user_creation_status', function(req, resp) {
@@ -233,7 +219,7 @@ function setup(app) {
});
function bcrypt_password(password, cb) {
- var bcryptWorkFactor = configuration.get('bcrypt_work_factor');
+ var bcryptWorkFactor = config.get('bcrypt_work_factor');
bcrypt.gen_salt(bcryptWorkFactor, function (err, salt) {
if (err) {
@@ -252,7 +238,7 @@ function setup(app) {
});
};
- app.post('/wsapi/complete_user_creation', checkParams(["token", "pass"]), function(req, resp) {
+ app.post('/wsapi/complete_user_creation', validate(["token", "pass"]), function(req, resp) {
// issue #155, valid password length is between 8 and 80 chars.
if (req.body.pass.length < 8 || req.body.pass.length > 80) {
httputils.badRequest(resp, "valid passwords are between 8 and 80 chars");
@@ -292,26 +278,34 @@ function setup(app) {
});
});
- app.post('/wsapi/stage_email', checkAuthed, checkParams(["email", "site"]), function (req, resp) {
- try {
- // on failure stageEmail may throw
- db.stageEmail(req.session.authenticatedUser, req.body.email, function(secret) {
+ app.post('/wsapi/stage_email', checkAuthed, validate(["email", "site"]), function (req, resp) {
+ db.lastStaged(req.body.email, function (last) {
+ if (last && (new Date() - last) < config.get('min_time_between_emails_ms')) {
+ logger.warn('throttling request to stage email address ' + req.body.email + ', only ' +
+ ((new Date() - last) / 1000.0) + "s elapsed");
+ return httputils.forbidden(resp, "throttling. try again later.");
+ }
- // store the email being added in session data
- req.session.pendingAddition = secret;
+ try {
+ // on failure stageEmail may throw
+ db.stageEmail(req.session.authenticatedUser, req.body.email, function(secret) {
- resp.json({ success: true });
+ // store the email being added in session data
+ req.session.pendingAddition = secret;
- // let's now kick out a verification email!
- email.sendAddAddressEmail(req.body.email, req.body.site, secret);
- });
- } catch(e) {
- // we should differentiate tween' 400 and 500 here.
- httputils.badRequest(resp, e.toString());
- }
+ resp.json({ success: true });
+
+ // let's now kick out a verification email!
+ email.sendAddAddressEmail(req.body.email, req.body.site, secret);
+ });
+ } catch(e) {
+ // we should differentiate tween' 400 and 500 here.
+ httputils.badRequest(resp, e.toString());
+ }
+ });
});
- app.get('/wsapi/email_for_token', checkParams(["token"]), function(req,resp) {
+ app.get('/wsapi/email_for_token', validate(["token"]), function(req,resp) {
db.emailForVerificationSecret(req.query.token, function(email) {
resp.json({ email: email });
});
@@ -357,7 +351,7 @@ function setup(app) {
});
});
- app.post('/wsapi/complete_email_addition', checkParams(["token"]), function(req, resp) {
+ app.post('/wsapi/complete_email_addition', validate(["token"]), function(req, resp) {
db.gotVerificationSecret(req.body.token, undefined, function(e) {
if (e) {
logger.warn("couldn't complete email verification: " + e);
@@ -368,7 +362,7 @@ function setup(app) {
});
});
- app.post('/wsapi/authenticate_user', checkParams(["email", "pass"]), function(req, resp) {
+ app.post('/wsapi/authenticate_user', validate(["email", "pass"]), function(req, resp) {
db.checkAuth(req.body.email, function(hash) {
if (typeof hash !== 'string' ||
typeof req.body.pass !== 'string')
@@ -387,7 +381,7 @@ function setup(app) {
// if the work factor has changed, update the hash here. issue #204
// NOTE: this runs asynchronously and will not delay the response
- if (configuration.get('bcrypt_work_factor') != bcrypt.get_rounds(hash)) {
+ if (config.get('bcrypt_work_factor') != bcrypt.get_rounds(hash)) {
logger.info("updating bcrypted password for email " + req.body.email);
bcrypt_password(req.body.pass, function(err, hash) {
db.updatePassword(req.body.email, hash, function(err) {
@@ -403,7 +397,7 @@ function setup(app) {
});
});
- app.post('/wsapi/remove_email', checkAuthed, checkParams(["email"]), function(req, resp) {
+ app.post('/wsapi/remove_email', checkAuthed, validate(["email"]), function(req, resp) {
var email = req.body.email;
db.removeEmail(req.session.authenticatedUser, email, function(error) {
@@ -425,10 +419,10 @@ function setup(app) {
}});
});
- app.post('/wsapi/cert_key', checkAuthed, checkParams(["email", "pubkey"]), function(req, resp) {
+ app.post('/wsapi/cert_key', checkAuthed, validate(["email", "pubkey"]), function(req, res) {
db.emailsBelongToSameAccount(req.session.authenticatedUser, req.body.email, function(sameAccount) {
// not same account? big fat error
- if (!sameAccount) return httputils.badRequest(resp, "that email does not belong to you");
+ if (!sameAccount) return httputils.badRequest(res, "that email does not belong to you");
// parse the pubkey
var pk = ca.parsePublicKey(req.body.pubkey);
@@ -436,13 +430,30 @@ function setup(app) {
// same account, we certify the key
// we certify it for a day for now
var expiration = new Date();
- expiration.setTime(new Date().valueOf() + configuration.get('certificate_validity_ms'));
+ expiration.setTime(new Date().valueOf() + config.get('certificate_validity_ms'));
var cert = ca.certify(req.body.email, pk, expiration);
- resp.writeHead(200, {'Content-Type': 'text/plain'});
- resp.write(cert);
- resp.end();
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.write(cert);
+ res.end();
+ });
+
+/* code to bounce the cert off of a proper keysigner (issue #460)
+
+ db.emailsBelongToSameAccount(req.session.authenticatedUser, req.body.email, function(sameAccount) {
+ // not same account? big fat error
+ if (!sameAccount) return httputils.badRequest(res, "that email does not belong to you");
+
+ // forward to the keysigner!
+ var keysigner = config.get('keysigner_url');
+ keysigner.path = '/wsapi/cert_key';
+ forward(keysigner, req, res, function(err) {
+ if (err) {
+ logger.error("error forwarding request:", err);
+ }
+ });
});
+*/
});
app.post('/wsapi/logout', function(req, resp) {
@@ -450,8 +461,7 @@ function setup(app) {
resp.json({ success: true });
});
- // in the cert world, syncing is not necessary,
- // just get a list of emails.
+ // returns a list of emails owned by the user
// returns:
// {
// "foo@foo.com" : {..properties..}
View
130 libs/configuration.js → lib/configuration.js
@@ -43,8 +43,11 @@
*/
const
-substitution = require('./substitute.js'),
-path = require('path');
+postprocess = require('postprocess'),
+path = require('path'),
+urlparse = require('urlparse'),
+secrets = require('./secrets'),
+temp = require('temp');
var g_config = {
};
@@ -71,9 +74,7 @@ const g_configs = { };
// production is the configuration that runs on our
// public service (browserid.org)
g_configs.production = {
- hostname: 'browserid.org',
- port: '443',
- scheme: 'https',
+ URL: 'https://browserid.org',
use_minified_resources: true,
var_path: '/home/browserid/var/',
database: {
@@ -83,31 +84,22 @@ g_configs.production = {
},
bcrypt_work_factor: 12,
authentication_duration_ms: (7 * 24 * 60 * 60 * 1000),
- certificate_validity_ms: (24 * 60 * 60 * 1000)
+ certificate_validity_ms: (24 * 60 * 60 * 1000),
+ min_time_between_emails_ms: (60 * 1000)
};
-// beta (diresworb.org) the only difference from production
-// is the hostname
-g_configs.beta = JSON.parse(JSON.stringify(g_configs.production));
-g_configs.beta.hostname = 'diresworb.org';
-
-// development (dev.diresworb.org) the only difference from production
-// is, again, the hostname
-g_configs.development = JSON.parse(JSON.stringify(g_configs.production));
-g_configs.development.hostname = 'dev.diresworb.org';
// local development configuration
g_configs.local = {
- hostname: '127.0.0.1',
- port: '10002',
- scheme: 'http',
+ URL: 'http://127.0.0.1:10002',
email_to_console: true, // don't send email, just dump verification URLs to console.
use_minified_resources: false,
var_path: path.join(__dirname, "..", "var"),
database: { driver: "json" },
bcrypt_work_factor: g_configs.production.bcrypt_work_factor,
authentication_duration_ms: g_configs.production.authentication_duration_ms,
- certificate_validity_ms: g_configs.production.certificate_validity_ms
+ certificate_validity_ms: g_configs.production.certificate_validity_ms,
+ min_time_between_emails_ms: g_configs.production.min_time_between_emails_ms
};
if (undefined !== process.env['NODE_EXTRA_CONFIG']) {
@@ -115,22 +107,21 @@ if (undefined !== process.env['NODE_EXTRA_CONFIG']) {
eval(fs.readFileSync(process.env['NODE_EXTRA_CONFIG']) + '');
}
-Object.keys(g_configs).forEach(function(config) {
- if (!g_configs[config].smtp) {
- g_configs[config].smtp = {
- host: process.env['SMTP_HOST'],
- user: process.env['SMTP_USER'],
- pass: process.env['SMTP_PASS']
- };
- }
-});
-
// test environments are variations on local
g_configs.test_json = JSON.parse(JSON.stringify(g_configs.local));
-g_configs.test_json.database = { driver: "json", unit_test: true };
+g_configs.test_json.database = {
+ driver: "json",
+ // use a temporary path for testing
+ path: temp.path({suffix: '.db'})
+};
g_configs.test_mysql = JSON.parse(JSON.stringify(g_configs.local));
-g_configs.test_mysql.database = { driver: "mysql", user: "test", unit_test: true };
+g_configs.test_mysql.database = {
+ driver: "mysql",
+ user: "test",
+ database: "browserid_" + secrets.generate(6),
+ create_schema: true
+};
// default deployment is local
if (undefined === process.env['NODE_ENV']) {
@@ -141,13 +132,58 @@ g_config = g_configs[process.env['NODE_ENV']];