repointing users at supported repo

This commit is contained in:
Benson Wong 2015-07-10 10:01:07 -07:00
Родитель a2f5c4c82b
Коммит 2e7dfb6dcf
65 изменённых файлов: 1 добавлений и 5080 удалений

Просмотреть файл

@ -1,11 +0,0 @@
{
"processes": [
"server/bin/clortho"
],
"env": {
"CONFIG_FILES": "$HOME/config.json"
},
"hooks": {
"postdeploy": "scripts/gen_keys.js"
}
}

10
.gitignore поставляемый
Просмотреть файл

@ -1,10 +0,0 @@
.DS_Store
/server/config/*.json
/server/config/*.pem
/server/config/*.csr
/server/var
*~
#*#
locale
/node_modules
/rpmbuild

Просмотреть файл

@ -1,27 +0,0 @@
{
"passfail": false,
"maxerr": 100,
"node": true,
"forin": false,
"boss": true,
"noarg": true,
"undef": true,
"unused": true,
"browser": true,
"laxbreak": true,
"laxcomma": true,
"eqeqeq": true,
"eqnull": true,
"expr": true,
"indent": 2,
"white": false,
"predef": [
"exports",
"require",
"process"
],
"esnext": true,
"shadow": false,
"supernew": false,
"strict": false
}

Просмотреть файл

@ -1,16 +0,0 @@
language: node_js
before_install:
- sudo apt-get install libgmp3-dev
before_script:
- scripts/gen_keys.js
node_js:
- "0.8"
- "0.10"
notifications:
email:
- lloyd@hilaiel.com
- bwong@mozilla.com

Просмотреть файл

@ -1,31 +0,0 @@
rel2014_06_20.19.26.40:
* b75c6ba Get rid of merge commits in the changelog
* 6688d8b Add a Do Not Track policy
* 0c2612f Adding some missing properties to the package.json file
* c032b4b remove superfluous argument to getUserData()
* 2246335 refactor auth.js, improved command line testing tool, removed unneccesary LDAP reconnection
* 34c6637 fix unit tests, update pwdChangeTime to pwdChangedTime
* d734bc5 Fix for issue #114
rel2013_08_28.15.22.40:
* 7129101 changed pwdChangeTime to pwdChangedTime
* 29fa434 Improve documentation on new release process, #111
* 25f4285 Remove old train/stage tag create scripts #111
* eb8979b update useragent to 2.0.7 to detect IE11
* 6140471 added bigint lockdown dependency back in
* fd8f2dc Detect 3rd party cookies and show an error screen
* 85f61de Make email address field read only after password manager runs. Fixes Issue #104
* e89639b #91 old session destroyed on password change
* 5657e90 fixes #91 User should be logged out when LDAP password changes
rel2013_08_06.15.01.46:
* 9a8ad5e Fix bugs in create-release.sh #111
* b41df8c Rename create-release-tag.sh to create-release.sh #111
* 737770d Add ChangeLog creation to create-release-tag.sh
* 5ea2b29 update readme
* ec24abf fix i18n-abide lockdown issues
* 794c302 #105 add P3P cookie if talking to IE browsers

Просмотреть файл

@ -1,36 +1 @@
[![Build
Status](https://travis-ci.org/mozilla/vinz-clortho.png?branch=master)](https://travis-ci.org/mozilla/vinz-clortho)
# Mozilla IdP
``mozilla-idp`` is a server that implements support for Persona on the
mozilla.com domain.
When deployed, this will allow mozillans with `mozilla.com` or
`mozillafoundation.org` email addresses to authenticate with Persona enabled
websites using their Mozilla (LDAP) password.
## Getting Code to Production
This is the process for getting new code into Production
1. Do features and bug fixes in branches. Create a pull request to have new
code merged into the `master` branch
1. Create a new release tag with `scripts/create-release.sh`
1. Create an RPM from the tag
1. Create a new staging server based on the new RPM
1. Have QA test to make sure everything is OK
1. *if* tests pass, create new production systems from same version. Otherwise
go back to step 1. to fix issues.
## Why the RPM?
This is a quick introduction to how the Service Ops. team deploys Mozilla IdP.
For security and operational reasons we turn the application into an RPM and
deploy from our private RPM repository.
This allows us to maintain a package that can be audited as well as very
specific versioning using RPMs.
The scripts and processes for building the RPM exists in the
mozilla-services/svcops-oompaloompas repository.
Moved to [mozilla-services/mozilla-idp](https://github.com/mozilla-services/mozilla-idp)

Просмотреть файл

@ -1,40 +0,0 @@
# Developer Notes
## Running locally
### Set up Configuration
copy server/config/local.json-dist to local.json and edit a few fields:
...
"ldap_bind_dn": "mail=USERNAME@mozilla.com,o=com,dc=mozilla",
...
"ldap_server_url": "ldap://localhost:1389",
...
### Starting the Mock LDAP server
$ QAUser=test QAPass=test node mock-ldap-server/server.js
This mock server runs LDAP on localhost:1389 and a status HTTP server on localhost:3001.
### Starting Vinz Clortho
$ PORT=3002 ADDRESS=0.0.0.0 npm start
### One Time Setup
We need our well-known to be on the file system. You can put this anywhere, but we'll use this when starting BrowserID servers.
$ curl http://localhost:3002/.well-known/browserid > [LOCAL_PATH_TO_WELL_KNOWN_FILE]
### Starting BrowserID
In your local browserid, set `SHIMMED_PRIMARIES`. Example:
$ cd ../browserid
$ SHIMMED_PRIMARIES="mozilla.com|http://127.0.0.1:3002|[LOCAL_PATH_TO_WELL_KNOWN_FILE]" npm start
You can now use `user1@mozilla.com` with password testtest and Vinz Clortho will handle the sign in flow.
Note: 127.0.0.1 should be an IP address that your web browser can use to hit your instance of Vinz Clortho. If your using VMs, then make it an IP for the VM.

Двоичные данные
docs/aws-infrastructure.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 32 KiB

Просмотреть файл

@ -1,32 +0,0 @@
This project uses statsd to report application level metrics.
The goal of statsd is to help you verify the application is
running correctly, and to facilitate problem isolation when
it's not.
Here is the statsd hierarchy:
* `mozillaidp` - top level bucket for all stats
* `ldap` - stats related to communication with LDAP
* `error` - counter of specific types of LDAP errors
* `auth` - counter of authentication attempts (success & failure)
* `timing` - granular timing for interactions with LDAP
* `server` - high level application events
* `uncaught_exception` - a js exception was thrown that wasn't caught
* `started` - the server started up
* `healthcheck` - status of health check when requested by load balancers
* `ok`
* `error`
* `provision` - stats about certificate provisioning
* `attempt` - HTML provisioning content was served
* `success` - certificate provisioning succeeded
* `auth` - stats about user authenticatoin
* `attempt` - HTML authentication content was served
* `throttle` - authentication failed due to throttling
* `success` - authentication succeeded and session was established
The ultimate source of truth is always the code, but an effort
should be made to keep this document up to date to describe the general
hierarchy so that statsd data is discoverable and easy to use as a
problem isolation tool.

Просмотреть файл

@ -1,474 +0,0 @@
{
"JSONSelect": {
"0.4.0": "a08edcc67eb3fcbe99ed630855344a0cf282bb8d"
},
"abbrev": {
"1.0.4": "bd55ae5e413ba1722ee4caba1f6ea10414a59ecd"
},
"amdefine": {
"0.0.5": "86b6e9470f8cde955ef7daa3cf5d544ba81aa3db"
},
"ansi": {
"0.1.2": "2627e29498f06e2a1c2ece9c21e28fd494430827"
},
"asn1": {
"0.1.11": "559be18376d08a4ec4dbe80877d27818639b2df7"
},
"assert-plus": {
"0.1.2": "d93ffdbb67ac5507779be316a7d65146417beef8"
},
"async": {
"0.1.22": "0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061",
"0.2.9": "df63060fbf3d33286a76aaf6d55a2986d9ff8619"
},
"aws-lib": {
"0.0.5": "971b8995078d83c80f2372f134c496e71b293a46"
},
"aws-sign": {
"0.2.0": "c55013856c8194ec854a0cbec90aab5a04ce3ac5"
},
"awsbox": {
"0.4.5": "027a9998955f09f3d6b0a10d38ea37ae381b6358"
},
"bigint": {
"0.4.2": "5bccafb5af3924cc71eb59942fb3a64a12906e49"
},
"boom": {
"0.3.8": "c8cdb041435912741628c044ecc732d1d17c09ea"
},
"browserify": {
"1.13.5": "b5f0a160733779d27547885dfb598a65ef6fdaad"
},
"buffertools": {
"1.1.0": "c1bc946a3cce722c9adaaed505e9896aa171be9c"
},
"bunyan": {
"0.21.1": "ea00a0d5223572e31e1e71efba2237cb1915942a"
},
"cef": {
"0.3.3": "74a521e3c5c702fe1dc240e6250a2583f885227f"
},
"character-parser": {
"1.0.2": "55384d6afcf8c6b9dd483e8347646a790e4545e7"
},
"cjson": {
"0.2.1": "73cd8aad65d9e1505f9af1744d3b79c1527682a5"
},
"cli": {
"0.4.4-2": "a38c8f991df22eea0179ec16dd90fa3f3c85fa4a"
},
"client-sessions": {
"0.3.0": "6c9993858b2009416247ae70051b88a1d5e6ee7d"
},
"coffee-script": {
"1.6.3": "6355d32cf1b04cdff6b484e5e711782b2f0c39be"
},
"colors": {
"0.5.1": "7d0023eaeb154e8ee9fce75dcb923d0ed1667774",
"0.6.0-1": "6dbb68ceb8bc60f2b313dcc5ce1599f06d19e67a"
},
"combined-stream": {
"0.0.4": "2d1a43347dbe9515a4a2796732e5b88473840b22"
},
"commander": {
"0.6.1": "fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06",
"1.1.1": "50d1651868ae60eccff0a2d9f34595376bc6b041"
},
"commondir": {
"0.0.1": "89f00fdcd51b519c578733fec563e6a6da7f5be2"
},
"connect": {
"1.9.2": "42880a22e9438ae59a8add74e437f58ae8e52807"
},
"connect-fonts": {
"0.0.9": "3233b93bb78b6c7c1818892b90fd779c83447d84"
},
"connect-fonts-opensans": {
"0.0.4": "184ce9f26b681a10a916268b1f80c4b6c9450e71"
},
"console-browserify": {
"0.1.6": "d128a3c0bb88350eb5626c6e7c71a6f0fd48983c"
},
"convict": {
"0.1.0": "d4b1948235196091855aaedc175374c58ae212d8"
},
"cookie-jar": {
"0.2.0": "64ecc06ac978db795e4b5290cbe48ba3781400fa"
},
"cookies": {
"0.2.1": "fee635ef023704893ac30387899f3dc448a52840"
},
"cryptiles": {
"0.1.3": "1a556734f06d24ba34862ae9cb9e709a3afbff1c"
},
"crypto-browserify": {
"0.2.3": "c98141505d90e31a1e456cb97343dc3b0f4a1a2a"
},
"css": {
"1.0.8": "9386811ca82bccc9ee7fb5a732b1e2a317c8a3e7"
},
"css-parse": {
"1.0.4": "38b0503fbf9da9f54e9c1dbda60e145c77117bdd"
},
"css-stringify": {
"1.0.5": "b0d042946db2953bb9d292900a6cb5f6d0122031"
},
"cycle": {
"1.0.2": "269aca6f1b8d2baeefc8ccbc888b459f322c4e60"
},
"dateformat": {
"1.0.2-1.2.3": "b0220c02de98617433b72851cf47de3df2cdbee9"
},
"debug": {
"0.7.2": "056692c86670977f115de82917918b8e8b9a10f0"
},
"delayed-stream": {
"0.0.5": "d4b1f43a93e8296dfe02694f4680bc37a313c73f"
},
"deputy": {
"0.0.4": "edc00a9ef5c53527c405328534c99795ada41cbf"
},
"detective": {
"0.1.1": "f1e04fe973754c8907ae51edd3e230e380d76fe9",
"0.2.1": "9ce92601fd223810c29432ad034f8c62d8b8654f"
},
"diff": {
"1.0.2": "4ae73f1aee8d6fcf484f1a1ce77ce651d9b7f0c9"
},
"dtrace-provider": {
"0.2.8": "e243f19219aa95fbf0d8f2ffb07f5bd64e94fe20"
},
"ejs": {
"0.6.1": "16ccc98eeeac166982927fa67eb3fca8865f6871"
},
"escodegen": {
"0.0.23": "9acf978164368e42276571f18839c823b3a844df"
},
"esprima": {
"0.9.9": "1b90925c975d632d7282939c3bb9c3a423c30490",
"1.0.3": "7bdb544f95526d424808654d3b8fbe928650c0fe"
},
"estraverse": {
"0.0.4": "01a0932dfee574684a598af5a67c3bf9b6428db2",
"1.1.2-1": "28ff59bb3cf1272a2027e1c1541f94b23226b311"
},
"express": {
"2.5.11": "4ce8ea1f3635e69e49f0ebb497b6a4b0a51ce6f0"
},
"extsprintf": {
"1.0.0": "4d58b815ace5bebfc4ebf03cf98b0a7604a99b86"
},
"eyes": {
"0.1.8": "62cf120234c683785d902348a800ef3e0cc20bc0"
},
"filed": {
"0.1.0": "b0f626472a2366dc1194537a4eea7e7a89f3c735"
},
"forEachAsync": {
"2.2.0": "093b32ce868cb69f5166dcf331fae074adc71cee"
},
"forever-agent": {
"0.2.0": "e1c25c7ad44e09c38f233876c76fcc24ff843b1f"
},
"form-data": {
"0.0.10": "db345a5378d86aeeb1ed5d553b869ac192d2f5ed"
},
"formidable": {
"1.0.14": "2b3f4c411cbb5fdd695c44843e2a23514a43231a"
},
"github-url-from-git": {
"1.1.1": "1f89623453123ef9623956e264c60bf4c3cf5ccf"
},
"glob": {
"3.2.6": "28c805b47bc6c19ba3059cbdf079b98ff62442f2"
},
"gobbledygook": {
"0.0.3": "437bb23d3ade04dd26b49e7c21e4026c6b086234"
},
"graceful-fs": {
"1.1.14": "07078db5f6377f6321fceaaedf497de124dc9465",
"1.2.3": "15a4806a57547cb2d2dbf27f42e89a8c3451b364"
},
"growl": {
"1.7.0": "de2d66136d002e112ba70f3f10c31cf7c350b2da"
},
"hawk": {
"0.10.2": "9b361dee95a931640e6d504e05609a8fc3ac45d2"
},
"hoek": {
"0.7.6": "60fbd904557541cd2b8795abf308a1b3770e155a"
},
"http-browserify": {
"0.1.1": "d9d82735a5f85f950761ac3909ba9485ec0af4f1"
},
"i18n-abide": {
"0.0.11": "0335f84ab550de1fc211b3169a105ac573f6efcc"
},
"inherits": {
"2.0.0": "76c81b3b1c10ddee3a60bf2c247162bc369f8ba8"
},
"is-promise": {
"1.0.0": "b998d17551f16f69f7bd4828f58f018cc81e064f"
},
"jade": {
"0.26.3": "8f10d7977d8d79f2f6ff862a81b0513ccb25686c",
"0.30.0": "9ea816da61bea6fef4709b4d9f6b442f1e205ba3"
},
"jshint": {
"2.1.2": "1fb7b77df7b8620c17d4f807945bb2d24990969e"
},
"json-schema": {
"0.2.2": "50354f19f603917c695f70b85afa77c3b0f23506"
},
"json-stringify-safe": {
"3.0.0": "9db7b0e530c7f289c5e8c8432af191c2ff75a5b3"
},
"jsprim": {
"0.3.0": "cd13466ea2480dbd8396a570d47d31dda476f8b1"
},
"jsxgettext": {
"0.1.3": "db9dd0bedd531606cdd5fd978dd75d383fd99327"
},
"jwcrypto": {
"0.4.3": "b48ef3db136b09df0933bfa09c717fd738b56038"
},
"keypress": {
"0.1.0": "4a3188d4291b66b4f65edb99f806aa9ae293592a"
},
"ldapjs": {
"0.6.3": "*"
},
"lockdown": {
"0.0.5": "6bbcc01b7d2a3894a712f135b2675a078ede24c7"
},
"lru-cache": {
"2.2.4": "6c658619becf14031d0d0b594b16042ce4dc063d",
"2.3.0": "1cee12d5a9f28ed1ee37e9c332b8888e6b85412a"
},
"mersenne": {
"0.0.3": "c39a3d45fee6091189ccd329107312ea8fe14d8a"
},
"mime": {
"1.2.10": "066380acbc3d78d4f4a51004d8988425dc68b9b1",
"1.2.4": "11b5fdaf29c2509255176b80ad520294f5de92b7",
"1.2.9": "009cd40867bd35de521b3b966f04e2f8d4d13d09"
},
"minimatch": {
"0.2.12": "ea82a012ac662c7ddfaa144f1c147e6946f5dafb"
},
"minimist": {
"0.0.2": "3297e0500be195b8fcb56668c45b925bc9bca7ab"
},
"mkdirp": {
"0.3.0": "1bbf5ab1ba827af23575143490426455f481fe1e",
"0.3.3": "595e251c1370c3a68bab2136d0e348b8105adf13",
"0.3.5": "de3e5f8961c88c787ee1368df849ac4413eca8d7"
},
"mocha": {
"1.9.0": "141054b13cb03ce5ce59aece3d65d5ca01b8df0a"
},
"moment": {
"1.7.2": "e66be344be2e9ec1d12f1e16a8ca49bf63417f4f"
},
"monocle": {
"0.1.50": "9a7cbd0ccc10de95fd78a04b9beb2482ae4940b7"
},
"ms": {
"0.3.0": "03edc348d613e66a56486cfdac53bcbe899cbd61"
},
"mv": {
"0.0.5": "15eac759479884df1131d6de56bce20b654f5391"
},
"negotiator": {
"0.2.6": "28db6bc2e442c8655325d156ff74055dc0db289c"
},
"node-font-face-generator": {
"0.1.0-dev1": "778430ea7fb61ed9de61e8a51e0aadda29007193"
},
"node-proxy": {
"0.6.0": "41e64712dbd947aa9d9f466b5c0b5ee020bbcbbb"
},
"node-statsd": {
"0.0.2": "*"
},
"node-uuid": {
"1.4.0": "07f9b2337572ff6275c775e1d48513f3a45d7a65"
},
"nomnom": {
"1.5.2": "f4345448a853cfbd5c0d26320f2477ab0526fe2f"
},
"nopt": {
"2.1.1": "91eb7c4b017e7c00adcad1fd6d63944d0fdb75c1"
},
"normalize-package-data": {
"0.1.7": "f216a318a868214892e89ad80a944cddc1a79615"
},
"npmlog": {
"0.0.4": "a12a7418606b7e0183a2851d97a8729b9a0f3837"
},
"nub": {
"0.0.0": "b369bd32bdde66af59605c3b0520bc219dccc04f"
},
"oauth-sign": {
"0.2.0": "a0e6a1715daed062f322b622b7fe5afd1035b6e2"
},
"once": {
"1.1.1": "9db574933ccb08c3a7614d154032c09ea6f339e7"
},
"oppressor": {
"0.0.1": "84b2058c9121c4c6fe632566b31ed39618bd8feb"
},
"optimist": {
"0.2.6": "c15b750c98274ea175d241b745edf4ddc88f177b",
"0.3.1": "6680d30560193af5a55eb64394883ed7bcb98f2e",
"0.3.4": "4d6d0bd71ffad0da4ba4f6d876d5eeb04e07480b",
"0.3.7": "c90941ad59e4273328923074d2cf2e7cbc6ec0d9",
"0.6.0": "69424826f3405f79f142e6fc3d9ae58d4dbb9200"
},
"osenv": {
"0.0.3": "cd6ad8ddb290915ad9e22765576025d411f29cb6"
},
"pkginfo": {
"0.3.0": "726411401039fe9b009eea86614295d5f3a54276"
},
"plist": {
"0.4.3": "812842a873aa57f98d0050bee6aaf3549b9d5798"
},
"pooling": {
"0.4.4": "d06d7f565e9a1eec8b60bfa259c1e958a9e6ba03"
},
"promise": {
"2.0.0": "46648aa9d605af5d2e70c3024bf59436da02b80e"
},
"qs": {
"0.4.2": "3cac4c861e371a8c9c4770ac23cda8de639b8e5f",
"0.5.6": "31b1ad058567651c526921506b9a8793911a0384"
},
"read-installed": {
"0.0.1": "2d9b9086ae33ae42793210f519701169edabd2e2"
},
"read-package-json": {
"0.4.1": "a0e917cce8333444889543de9db2b1a4606baaa2"
},
"readdirp": {
"0.2.5": "c4c276e52977ae25db5191fe51d008550f15d9bb"
},
"relative-date": {
"1.1.1": "75c97c5446fa1146c1d250c47ca3629fb9a2e764"
},
"request": {
"2.16.6": "872fe445ae72de266b37879d6ad7dc948fa01cad"
},
"resolve": {
"0.2.8": "fdb17d4abb0ecaf6f80d67ac03cf290088f6c0d0"
},
"response-stream": {
"0.0.0": "da4b17cc7684c98c962beb4d95f668c8dcad09d5"
},
"rimraf": {
"2.1.4": "5a6eb62eeda068f51ede50f29b3e5cd22f3d9bb2"
},
"sax": {
"0.1.5": "d1829a6120fa01665eb4dbff6c43f29fd6d61471",
"0.5.4": "a3a4e1a9cf182bb547156c5232a49a1c3732ff7d"
},
"semver": {
"1.0.14": "cac5e2d55a6fbf958cb220ae844045071c78f676"
},
"sequence": {
"2.2.1": "7f5617895d44351c0a047e764467690490a16b03"
},
"shelljs": {
"0.1.4": "dfbbe78d56c3c0168d2fb79e10ecd1dbcb07ec0e"
},
"should": {
"1.2.2": "0f03f775066d9ea2632690c917b12824fcc1d582"
},
"sigmund": {
"1.0.0": "66a2b3a749ae8b5fb89efd4fcc01dc94fbe02296"
},
"slide": {
"1.1.4": "2b23f1949b369ed61d22bd6570ff0320302fc8df"
},
"sntp": {
"0.1.4": "5ef481b951a7b29affdf4afd7f26838fc1120f84"
},
"source-map": {
"0.1.27": "f114e06a8b5c05cbc51aa1fa600e728162455eda"
},
"stack-trace": {
"0.0.7": "c72e089744fc3659f508cdce3621af5634ec0fff"
},
"temp": {
"0.4.0": "671ad63d57be0fe9d7294664b3fc400636678a60",
"0.5.1": "77ab19c79aa7b593cbe4fac2441768cad987b8df"
},
"through": {
"0.1.4": "8059576d833089b3c18eccd37580bf9cc5b59130"
},
"tmp": {
"0.0.16": "704ea0513c15375389ef5b6dde865730528f2245"
},
"transformers": {
"2.0.1": "352131dfceb93a7532dc7535a4f142510435a394"
},
"tunnel-agent": {
"0.2.0": "6853c2afb1b2109e45629e492bde35f459ea69e8"
},
"ua-parser": {
"0.2.4": "c25ef577be95350d0fe1d3361597282da4e9ba71"
},
"uglify-js": {
"1.0.6": "f0d3aafd463f26a437b9ebc19f4947ab7e8078aa",
"1.2.6": "d354b2d3c1cf10ebc18fa78c11a28bdd9ce1580d",
"2.2.5": "a6e02a70d839792b9780488b7b8b184c095c99c7"
},
"underscore": {
"1.1.7": "40bab84bad19d230096e8d6ef628bff055d83db0",
"1.4.4": "61a6a32010622afa07963bf325203cf12239d604"
},
"urlparse": {
"0.0.1": "d171ec4681fcd0d8bd00b64345637d89a9700876"
},
"useragent": {
"2.0.7": "a44c07d157a15e13d73d4af4ece6aab70f298224"
},
"validator": {
"0.4.24": "9d88e88809114a861ec69bc0ddcb85322c98499a",
"1.1.3": "0ed8cb7c3b0b8852bb853b09ef8fcde1d1d4545b"
},
"vasync": {
"1.3.3": "84917680717020b67e043902e63bc143174c8728"
},
"verror": {
"1.1.0": "2a4b4eb14a207051e75a6f94ee51315bf173a1b0",
"1.3.3": "8a6a4ac3a8c774b6f687fece49bdffd78552e2cd"
},
"vm-browserify": {
"0.0.1": "51d25979366ab219dfe35a3fc65ecd6af3631d54"
},
"vows": {
"0.5.13": "f6fd9ee9c36d3f20bd6680455cff8090c4b29cde"
},
"walk": {
"2.2.1": "5ada1f8e49e47d4b7445d8be7a2e1e631ab43016"
},
"winston": {
"0.7.1": "e291ab24eddbf79ea40ff532619277a0d30b0eb3"
},
"wordwrap": {
"0.0.2": "b79669bb42ecb409f83d583cad52ca17eaa1643f"
},
"xml2js": {
"0.1.13": "438ff3b1d85a51ad659ffc2ebe83403e10c98722"
},
"xmlbuilder": {
"0.4.2": "1776d65f3fdbad470a08d8604cdeb1c4e540ff83"
},
"xmldom": {
"0.1.16": "cf2602832b1ab5c3e6813fca08fe70196ba15e8c"
},
"yamlparser": {
"0.0.2": "32393e6afc70c8ca066b6650ac6738b481678ebc"
}
}

Просмотреть файл

@ -1,10 +0,0 @@
# About
This is a fake LDAP server that makes it convenient to test the LDAP connection and
binding functionality without spinning up and populating a real ldap server.
## Usage
> node mock-ldap-server/server.js
This will start the server and have it listen on the default LDAP port: 1389

Просмотреть файл

@ -1,197 +0,0 @@
#!/usr/bin/env node
const util = require("util"),
path = require("path"),
express = require('express');
/**
* This provides *TWO*, yes *TWO* servers.
*
* 1. the Mock LDAP server that provides some basic
* users for testing
*
* 2. a HTTP server that provides an interface for changing
* LDAP data and various other tweaks to make Q/A easier
*/
var ldap = require("ldapjs");
var createLdapMock = require("../server/lib/ldapMock");
var ldapMock = createLdapMock();
var fakeLatency = 1;
var LDAP_LISTENING = false;
var ldapServer = null;
function resetServer() {
if (ldapServer && ldapServer.close) {
try { ldapServer.close(); } catch (e) {}
}
ldapServer = ldap.createServer();
ldapServer.bind('dc=mozilla', function(req, res, next) {
if (fakeLatency == -1 ) return;
setTimeout(function() {
ldapMock.bindHandler.call(ldapServer, req, res, next);
}, fakeLatency);
});
ldapServer.search('dc=mozilla', function(req, res, next) {
if (fakeLatency == -1 ) return;
setTimeout(function() {
ldapMock.searchHandler.call(ldapServer, req, res, next);
}, fakeLatency);
});
ldapServer.on('bind', function(bindEvent) {
console.log(util.format("Bind Event: Success - %s, dn: %s, credentials: %s",
bindEvent.success, bindEvent.dn, bindEvent.credentials));
});
ldapServer.on('authorize', function(e) {
console.log(util.format("Auth %s, dn: %s", (e.success) ? "OK" : "FAIL", e.dn));
});
ldapServer.on('listening', function() {
console.log("LDAP SERVER LISTENING on port: " + ldapServer.port);
});
ldapServer.on('close', function() {
LDAP_LISTENING = false;
console.log("LDAP SERVER STOPPED LISTENING");
});
}
function startLDAP() {
if (LDAP_LISTENING === true) {
return;
}
LDAP_LISTENING = true;
ldapServer.listen(1389, function() {
ldapServer.emit("listening");
});
}
resetServer();
startLDAP();
/**
* HTTP Back-channel server for mucking w/ things ;)
*/
if (!process.env.QAUser || !process.env.QAPass) {
console.log("NO QAUser/QAPass set");
process.exit(1)
} else {
var QAUser = process.env.QAUser,
QAPass = process.env.QAPass;
}
app = express.createServer();
app.use(express.basicAuth(QAUser, QAPass));
app.use(express.bodyParser());
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
});
app.get('/', function(req, res, next) {
res.render("main", {
latency: fakeLatency,
state: LDAP_LISTENING,
directory: ldapMock.directory
});
});
app.post('/reset', function(req, res, next) {
fakeLatency = 1;
ldapMock = createLdapMock();
resetServer();
startLDAP();
res.redirect('/');
});
// Updates the LDAP database of users
app.post('/update-users', function(req, res, next) {
var dir = ldapMock.directory;
for (var email in req.body) {
for(var i=0; i<dir.length;i++) {
if (dir[i].attributes.mail == email) {
dir[i].attributes.password = req.body[email];
}
}
}
res.redirect('/');
});
// Changes how long the LDAP server will wait to respond
app.post('/set-latency', function(req, res, next) {
fakeLatency = parseInt(req.body.latency, "10");
res.redirect('/');
});
// Sets LDAP state up/down
app.post('/set-state', function(req, res, next) {
if (req.body.state == "up") {
startLDAP();
} else {
ldapServer.close();
}
res.redirect('/');
});
// resets everything back to defaults
app.post('/reset-all', function(req, res, next) {
});
app.listen('3001', function(err) {
if (err) {
console.error("ERROR", err);
} else {
console.log("Web Server running on 0.0.0.0:3001");
}
});
/* Example queries you can run!
Get all emails
---------------------
> ldapsearch -H ldap://localhost:1389 -x \
-D mail=user@mozilla.com,o=com,dc=mozilla -w testtest \
-LLL -b "dc=mozilla, o=com" mail=*
Search by email
---------------
> ldapsearch -H ldap://localhost:1389 -x \
-D mail=user@mozilla.com,o=com,dc=mozilla -w testtest \
-LLL -b "dc=mozilla, o=com" mail=user@mozilla.org
Bind wrong w/ invalid user
--------------------------
> ldapsearch -H ldap://localhost:1389 -x \
-D cn=not_here,o=com,dc=mozilla -w testtest \
-LLL -b "dc=mozilla, o=com" mail=user@mozilla.org
Bind wrong w/ bad password
--------------------------
> ldapsearch -H ldap://localhost:1389 -x \
-D cn=vinz,o=com,dc=mozilla -w OOPS \
-LLL -b "dc=mozilla, o=com" mail=user@mozilla.org
Don't bind at all
--------------------------
> ldapsearch -H ldap://localhost:1389 -x \
-LLL -b "dc=mozilla, o=com" mail=*
*/

Просмотреть файл

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LDAP Debugger</title>
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
</head>
<body>
<div id="content">
<%- body %>
</div>
</body>
</html>

Просмотреть файл

@ -1,64 +0,0 @@
<h1>LDAP Server Q/A Tool</h1>
<h2>RESET!</h2>
<form method="post" action="/reset">
<input type="submit" value="RESET BACK TO DEFAULTS">
</form>
<h2>Server Status</h2>
<form method="POST" action="/set-state">
<p>Current State: <span><%=((state==true)? "UP" : "DOWN")%></span></p>
<input type="radio" name="state" value="up" id="state_up"/>
<label for="state_up">UP</label>
<br/>
<input type="radio" name="state" value="down" id="state_down"/>
<label for="state_down">DOWN</label>
<br/>
<input type="submit" value="Change Server State">
</form>
<hr/>
<h2>User Editor</h2>
<form method="POST" action="/update-users">
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<th>Email Address</th>
<th>Password</th>
</tr>
<% for(var i=1; i<directory.length; i++) { %>
<% if(directory[i].attributes.mail.indexOf("mozilla") == -1) { continue;} %>
<tr>
<td><%=directory[i].attributes.mail%></td>
<td><input type="text" name="<%=directory[i].attributes.mail%>" value="<%=directory[i].attributes.password%>"></td>
</tr>
<% } %>
</table>
<br/>
<input type="submit" value="Update Users">
</form>
<hr/>
<h2>Response Latency</h2>
<form method="POST" action="/set-latency">
<p>Current Latency: <span><%=((latency == -1)? "Blackhole" : latency + "ms")%></span></p>
<select name="latency">
<option value="1">1 ms</option>
<option value="30">30 ms</option>
<option value="1000">1 second</option>
<option value="5000">5 seconds</option>
<option value="10000">10 seconds</option>
<option value="-1">Blackhole</option>
</select>
<input type="submit" value="Set Latency"/>
</form>

Просмотреть файл

@ -1,53 +0,0 @@
{
"author": "Ozten <shout@ozten.com> (http://ozten.com)",
"name": "clortho",
"description": "A server that provides Persona support for mozilla.{com,org} emails.",
"version": "0.0.0",
"licenses" : [{
"type": "MPL 2.0",
"url": "https://mozilla.org/MPL/2.0/"
}],
"homepage": "https://github.com/mozilla/vinz-clortho",
"bugs": "https://github.com/mozilla/vinz-clortho/issues",
"repository": {
"type": "git",
"url": "https://github.com/mozilla/vinz-clortho"
},
"main": "clortho",
"engines": {
"node": "~0.6.10"
},
"dependencies": {
"client-sessions": "0.3.0",
"convict": "0.1.0",
"ejs": "0.6.1",
"express": "2.5.11",
"i18n-abide": "0.0.11",
"jwcrypto": "0.4.3",
"ldapjs": "https://github.com/lloyd/node-ldapjs/archive/ephemeral_port.tar.gz",
"node-statsd": "https://github.com/downloads/lloyd/node-statsd/0509f85.tgz",
"winston": "0.7.1",
"cef": "0.3.3",
"connect-fonts": "0.0.9",
"connect-fonts-opensans": "0.0.4",
"lockdown": "0.0.5",
"validator": "1.1.3",
"underscore": "1.4.4",
"useragent": "2.0.7"
},
"devDependencies": {
"awsbox": "0.4.5",
"mocha": "1.9.0",
"should": "1.2.2",
"request": "2.16.6",
"jshint": "2.1.2",
"walk": "2.2.1",
"temp": "0.5.1",
"optimist": "0.6.0"
},
"scripts": {
"start": "LOCAL_DEV=true node ./server/bin/clortho",
"test": "LOCAL_DEV=true mocha -R spec tests/",
"preinstall": "node ./scripts/lockdown.js"
}
}

Просмотреть файл

@ -1,67 +0,0 @@
#
# so all of our tags are in a consistent naming format
#
#
# Make sure everything is clean
#
cd $(dirname $0)/../
BASE=$PWD
# Make sure there are no uncommited changes
# src: http://stackoverflow.com/a/3879077/445792
git update-index -q --ignore-submodules --refresh
err=0
# Disallow unstaged changes in the working tree
if ! git diff-files --quiet --ignore-submodules --
then
echo >&2 "cannot $1: you have unstaged changes."
git diff-files --name-status -r --ignore-submodules -- >&2
err=1
fi
# Disallow uncommitted changes in the index
if ! git diff-index --cached --quiet HEAD --ignore-submodules --
then
echo >&2 "cannot $1: your index contains uncommitted changes."
git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
err=1
fi
if [ $err = 1 ]
then
echo >&2 "Please commit or stash them."
exit 1
fi
RDATE=$(date '+%Y_%m_%d.%H.%M.%S')
TAG="rel$RDATE"
LAST_RELEASE=$(git tag --list | grep ^rel | sort -r | head -1)
if [ ! -e $BASE/ChangeLog ]; then
touch $BASE/ChangeLog
fi
if [ -z "$LAST_RELEASE" ]; then
echo "Error: No last release found." >&2
exit 1
fi
if [ -z $(git log --pretty=format:%s "$LAST_RELEASE..HEAD") ]; then
echo "Abort: No changes since $LAST_RELEASE" >&2
exit 1
fi
TMPFILE=$(mktemp /tmp/idpchangelog.XXXXX)
echo "$TAG:" > $TMPFILE
echo >> $TMPFILE
git log --no-merges --pretty=" * %h %s" "$LAST_RELEASE..HEAD" >> $TMPFILE
echo >> $TMPFILE
cat $BASE/ChangeLog >> $TMPFILE
cat $TMPFILE
cat $TMPFILE > $BASE/ChangeLog
git add $BASE/ChangeLog
git commit -am "Create Release: $TAG"
git tag $TAG

Просмотреть файл

@ -1,62 +0,0 @@
#!/bin/sh
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# About:
#
# This script generates a SRPM of the source + rpm spec file to be passed
# the mock RPM build tool. See: http://fedoraproject.org/wiki/Projects/Mock
#
set -e
progname=$(basename $0)
TOP="$(cd $(dirname $0)/..; pwd)" # top level of the checkout
cd $TOP
if [ $# -ne 1 ]; then
echo "Usage: $(basename $0) (GIT_SHA | GIT_TAG | GIT_BRANCH)"
exit 1
else
VER=$1
fi
rm -rf rpmbuild
mkdir -p rpmbuild/SRPM rpmbuild/SOURCES rpmbuild/SPECS rpmbuild/BUILD rpmbuild/TMP
git fetch
git clone . rpmbuild/TMP &>/dev/null
cd rpmbuild/TMP
git checkout $VER &>/dev/null
export GIT_REVISION=$(git log -1 --oneline)
export GIT_HASH=$(echo $GIT_REVISION | cut -d ' ' -f1)
export MOZIDP_VER="$(echo $VER | sed 's/-/_/g').$GIT_HASH"
# build a tarball so we can create the source RPM correctly
tar --exclude .git \
-czf "$TOP/rpmbuild/SOURCES/mozidp-$MOZIDP_VER.tar.gz" .
cd $TOP
set +e
# generate a new spec file with the version baked in
TMPFILE=$TOP/rpmbuild/SPECS/mozidp.spec
sed "s/__VERSION__/$MOZIDP_VER/g" scripts/mozidp.spec.template > $TMPFILE
echo "Building Source RPM"
mock --root epel-6-x86_64 \
--buildsrpm \
--spec $TMPFILE \
--sources $TOP/rpmbuild/SOURCES
FILENAME=mozilla-idp-server-${MOZIDP_VER}-1.el6.src.rpm
SRPM_SOURCE=/var/lib/mock/epel-6-x86_64/result/$FILENAME
if [ ! -e $SRPM_SOURCE ]; then
echo "Failed building SRPM" >&2
exit 1
fi
mv /var/lib/mock/epel-6-x86_64/result/$FILENAME $TOP/rpmbuild/SRPM/
echo "Wrote: $TOP/rpmbuild/SRPM/$FILENAME"

Просмотреть файл

@ -1,58 +0,0 @@
#!/usr/bin/env node
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* scripts/gen_keys.js creates public and private keys suitable for
key signing Persona Primary IdP's.
Usage:
scripts/gen_keys.js
Will create these files
server/config/public-key.json
server/config/secret-key.json
If these files already exist, this script will show an error message
and exit. You must remove both keys if you want to generate a new
keypair.
*/
const jwcrypto = require("jwcrypto")
, fs = require('fs')
, assert = require("assert")
, configDir = fs.realpathSync(__dirname + "/../server/config")
, pubKeyFile = configDir + "/public-key.json"
, secretKeyFile = configDir + "/secret-key.json" ;
require("jwcrypto/lib/algs/rs");
try {
assert(fs.existsSync(configDir), "Config dir" + configDir + " not found");
assert(! fs.existsSync(pubKeyFile), "public key file: ["+pubKeyFile+"] already exists");
assert(! fs.existsSync(secretKeyFile), "public key file: ["+secretKeyFile+"] already exists");
} catch(e) {
console.error("Error: " + e.message);
process.exit(1);
}
console.log("Generating keypair. (install libgmp if this takes more than a second)");
// wondering about `keysize: 256`?
// well, 257 = 2048bit key
// still confused? see: https://github.com/mozilla/jwcrypto/blob/master/lib/algs/ds.js#L37-L57
jwcrypto.generateKeypair(
{algorithm: 'RS', keysize: 256},
function(err, keypair) {
var pubKey = keypair.publicKey.serialize()
var secretKey = keypair.secretKey.serialize()
fs.writeFileSync(pubKeyFile, pubKey);
console.log("Public Key saved:", pubKeyFile);
fs.writeFileSync(secretKeyFile, secretKey);
console.log("Secret Key saved:", pubKeyFile);
}
);

Просмотреть файл

@ -1,192 +0,0 @@
#!/usr/bin/env node
if (process.env['NPM_LOCKDOWN_RUNNING']) process.exit(0);
console.log("NPM Lockdown is here to check your dependencies! Never fear!");
var http = require('http'),
crypto = require('crypto'),
exec = require('child_process').exec,
fs = require('fs'),
path = require('path');
try {
var lockdownJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'lockdown.json')));
} catch(e) {
console.log("\nERROR: I cannot read lockdown.json! run node_modules/.bin/lockdown-relock to generate!\n");
process.exit(1);
}
var boundPort;
// during execution fatal errors will be appended to this list
var errors = [];
// during execution non-fatal warnings will be appended to this list
var warn = [];
function rewriteURL(u) {
return u.replace('registry.npmjs.org', '127.0.0.1:' + boundPort);
}
function packageOk(name, ver, sha, required) {
if (!lockdownJson[name]) {
if (required) {
errors.push("package '" + name + "' not in lockdown.json!");
}
return false;
}
if (lockdownJson[name][ver] === undefined) {
if (required) {
errors.push("package version " + name + "@" + ver + " not in lockdown.json!");
}
return false;
}
// a '*' shasum is not checked
var wantSHA = lockdownJson[name][ver];
if (wantSHA !== '*' && wantSHA !== sha) {
if (required) {
errors.push("package " + name + "@" + ver + " has a different checksum (" +
wantSHA + " v. " + sha + ")");
}
return false;
}
if (wantSHA === '*') {
warn.push("Lockdown cannot guarantee your saftey! No sha for pkg " + name + "@" + ver +
" in lockdown.json");
}
return true;
}
function rewriteVersionMD(json) {
if (typeof json === 'string') json = JSON.parse(json);
if (!json.error) {
json.dist.tarball = rewriteURL(json.dist.tarball);
// is the name/version/sha in our lockdown.json?
if (!packageOk(json.name, json.version, json.dist.shasum, true)) return null;
}
return JSON.stringify(json);
}
function rewritePackageMD(json) {
if (typeof json === 'string') json = JSON.parse(json);
if (!json.error) {
Object.keys(json.versions).forEach(function(ver) {
var data = json.versions[ver];
var name = data.name;
var sha = data.dist ? data.dist.shasum : undefined;
if (packageOk(name, ver, sha, false)) {
data.dist.tarball = rewriteURL(data.dist.tarball);
} else {
delete json.versions[ver];
}
});
}
return JSON.stringify(json);
}
var server = http.createServer(function (req, res) {
if (req.method !== 'GET') {
return res.end('non GET requests not supported', 501);
}
// what type of request is this?
// 1. specific version json metadata (when explicit dependency is expressed)
// - for these requests we should verify the name/version/sha advertised is allowed
// 2. package version json metadata (when version range is expressed - including '*')
// XXX: for these requests we should prune all versions that are not allowed
// 3. tarball - actual bits
// XXX: for these requests we should verify the name/version/sha matches something
// allowed, otherwise block the transaction
var arr = req.url.substr(1).split('/');
var type = [ '', 'package_metadata', 'version_metadata', 'tarball' ][arr.length];
// let's extract pkg name and version sensitive to the type of request being performed.
var pkgname, pkgver;
if (type === 'tarball') {
pkgname = arr[0];
var getVer = new RegExp("^" + pkgname + "-(.*)\\.tgz$");
pkgver = getVer.exec(arr[2])[1];
} else if (type === 'version_metadata') {
pkgname = arr[0];
pkgver = arr[1];
} else if (type === 'package_metadata') {
pkgname = arr[0];
}
var hash = crypto.createHash('sha1');
var r = http.request({
host: 'registry.npmjs.org',
port: 80,
method: req.method,
path: req.url,
agent: false
}, function(rres) {
res.setHeader('Content-Type', rres.headers['content-type']);
if (type === 'tarball') res.setHeader('Content-Length', rres.headers['content-length']);
var b = "";
rres.on('data', function(d) {
hash.update(d);
if (type != 'tarball') b += d;
else res.write(d);
});
rres.on('end', function() {
if (type === 'tarball') {
res.end();
} else {
if (type === 'package_metadata') {
b = rewritePackageMD(b);
} else if (type === 'version_metadata') {
b = rewriteVersionMD(b);
}
if (b === null) {
res.writeHead(404);
res.end("package installation disallowed by lockdown");
} else {
res.setHeader('Content-Length', Buffer.byteLength(b));
res.writeHead(rres.statusCode);
res.end(b);
}
}
});
});
r.end();
});
server.listen(process.env['LOCKDOWN_PORT'] || 0, '127.0.0.1', function() {
boundPort = server.address().port;
var child = exec('npm install', {
env: {
NPM_CONFIG_REGISTRY: 'http://127.0.0.1:' + boundPort,
NPM_LOCKDOWN_RUNNING: "true",
PATH: process.env['PATH'],
HOME: process.env['HOME']
},
cwd: process.cwd()
}, function(e) {
if (warn.length) {
console.log();
console.log("LOCKDOWN WARNINGS:");
warn.forEach(function(e) { console.log(" ", e); });
console.log();
}
if (errors.length) {
console.log();
console.log("LOCKDOWN ERRORS:");
errors.forEach(function(e) { console.log(" ", e); });
console.log();
}
process.exit(e ? 1 : 0);
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
});

Просмотреть файл

@ -1,42 +0,0 @@
%define _rootdir /opt/mozilla-idp-server
%define version __VERSION__
Name: mozilla-idp-server
Version: %{version}
Release: 1%{?dist}
Summary: Mozilla IdP Server
Packager: Benson Wong <bwong@mozilla.com>
Group: Development/Libraries
License: MPL 2.0
URL: https://github.com/mozilla/vinz-clortho
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
AutoReqProv: no
Requires: openssl, nodejs == 0.8.24, gmp-devel
BuildRequires: gmp-devel, gcc-c++, git, make, npm, nodejs == 0.8.24
Source: mozidp-%{version}.tar.gz
%description
Mozilla IdP Server
%prep
%setup -q -c
%build
npm install
export PATH=$PWD/node_modules/.bin:$PATH
echo "$GIT_REVISION" > static/ver.txt
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_rootdir}
for f in node_modules docs mock-ldap-server scripts server static tests *.json *.md; do
cp -rp $f %{buildroot}%{_rootdir}/
done
mkdir -p %{buildroot}%{_rootdir}/config
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_rootdir}

Просмотреть файл

@ -1,75 +0,0 @@
#!/usr/bin/env node
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
process.on('uncaughtException', function(err) {
console.error('uncaught exception', err);
});
const ldap = require('ldapjs'),
config = require('../server/lib/configuration'),
path = require('path');
var argv = require('optimist')
.usage('Test authentication against LDAP.\nUsage: $0')
.alias('h', 'help')
.describe('h', 'display this usage message')
.alias('u', 'url')
.describe('u', 'LDAP server url')
.default('u', 'ldaps://addressbook.mozilla.com:636')
.alias('a', 'address')
.describe('a', 'email address to authenticate (may be an alias)')
.demand('a')
.alias('p', 'password')
.describe('p', 'LDAP account password')
.demand('p')
.alias('c', 'canonical')
.describe('c', 'canonical LDAP password (required when email is an alias)');
var args = argv.argv;
// request context (cookie jar, etc)
var ctx = {};
if (args.h) {
argv.showHelp();
process.exit(0);
}
var auth = require('../server/lib/auth');
var dn = auth.convertEmailToDN(args.c || args.a);
auth.canonicalAddress({
email: args.a,
dn: dn,
pass: args.p,
url: args.u
}, function(err, canonicalAddress) {
if (err) {
console.log(util.format("communication with LDAP failed (%s): %s",
err.name, err.message));
} else {
if (canonicalAddress == args.a) {
console.log(args.a, "is canonical");
} else {
console.log(util.format("canonical address for %s is %s",
args.a, canonicalAddress));
}
auth.authUser({
url: args.u,
email: canonicalAddress,
pass: args.p
}, function (err, passed) {
if (err) {
console.log(util.format("authentication with LDAP failed (%s): %s",
err.name, err.message));
} else {
console.log(passed);
}
});
}
});

Просмотреть файл

@ -1,179 +0,0 @@
#!/usr/bin/env node
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const path = require('path');
// ./server is our current working directory
process.chdir(path.dirname(__dirname));
const config = require('../lib/configuration'),
crypto = require('crypto'),
express = require('express'),
fs = require('fs'),
http = require('http'),
i18n = require('i18n-abide'),
p3p = require('../lib/p3p'),
routes = require('../routes').routes(),
sessions = require('client-sessions'),
statsd = require('../lib/statsd'),
util = require('util'),
logger = require('../lib/logging').logger,
font_middleware = require("connect-fonts"),
opensans = require("connect-fonts-opensans"),
validate = require('../lib/validate');
// log uncaught exceptions
process.on('uncaughtException', function(err) {
if (statsd) statsd.increment('server.uncaught_exception');
logger.error(err.stack || err);
});
var app;
app = express.createServer();
app.use(express.errorHandler());
app.set('views', path.join(process.cwd(), 'views'));
app.set('view engine', 'ejs');
// send web log messages to our logging abstraction
app.use(express.logger({
stream: {
write: function(message){
// trim newlines as our logger inserts them for us.
if (typeof message === 'string') message = message.trim();
logger.info(message);
}
}
}));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(function(req, res, next) {
// We will always be deployed behind an SSL terminator. This
// flag tells client-sessions that it's ok to send cookies which
// will only be returned over SSL.
req.connection.proxySecure = true;
if (!config.get('local_development')) {
// when we're not running local development, append STS headers to
// all requests to force browsers to use SSL
// expires in 30 days
res.setHeader("Strict-Transport-Security", "max-age=10886400; includeSubdomains");
}
next();
});
if (config.get('local_development')) {
logger.warn("Running with insecure cookies, not appropriate for production");
}
app.use(sessions({
cookieName: 'session',
secret: config.get('cookie.secret'),
duration: config.get('cookie.duration_ms'),
cookie: {
maxAge: config.get('cookie.duration_ms'),
httpOnly: true,
// only allow non-secure cookies during local development
secure: !config.get('local_development')
}
}));
// add p3p cookie support
app.use(p3p);
app.use(font_middleware.setup({
fonts: [ opensans ],
ua: "all",
maxage: 180 * 24 * 60 * 60 * 1000, // 180 days
compress: true,
allow_origin: "*"
}));
app.use(express.csrf());
app.use(i18n.abide({
supported_languages: config.get('supported_languages'),
default_lang: config.get('default_lang'),
debug_lang: config.get('debug_lang'),
locale_directory: config.get('locale_directory')
}));
// set up local variables available to templates
app.use(function(req, res, next) {
// delete the supplied CSRF token at this point, it's already been checked and
// subsequent API endpoints perform rigorous input validation.
if (req.body && req.body._csrf) delete req.body._csrf;
res.local('csrf_token', req.session._csrf);
res.local('browserid_server', config.get('browserid_server'));
var s = req.session || null;
res.local('session', s);
next();
});
// all API requests should be uncachable
app.use(function(req, res, next) {
if (/^\/api\//.test(req.url)) {
res.setHeader('Cache-Control', 'no-cache, max-age=0');
}
next();
});
// documents
app.get('/.well-known/browserid', routes.well_known_browserid);
app.get('/', routes.welcome);
app.get('/sign_in', routes.signin);
app.get('/provision', routes.provision);
// monitoring
app.get('/node_status', routes.elb_check);
app.get('/ldap_status', routes.checkStatus);
// APIs
app.get('/api/session_context', routes.session_context);
app.post('/api/sign_in', validate({
user: 'email',
pass: 'password'
}), routes.check_signin);
app.post('/api/provision', validate({
user: 'email',
pubkey: 'pubkey'
}), routes.provision_key);
// for testing
app.get('/signout', routes.signout);
// static files
app.use(express.static(path.join(process.cwd(), '..', 'static')));
app.use(routes.handle404);
var dntPolicy = fs.readFileSync(path.join(process.cwd(), '..', 'static', 'dnt-policy.txt')).toString();
app.get('/.well-known/dnt-policy.txt', function(req, res) {
res.setHeader('Content-Type', 'text/plain; charset=utf8');
res.send(dntPolicy);
});
function startup(cb) {
app.listen(config.get('http_port'), config.get('http_address'), function(err) {
statsd.increment('server.started');
if (cb) cb(err, app.address());
});
}
// allow clortho to be invoked from the command line or as a library
// ref: http://stackoverflow.com/a/6398335/445792
if (require.main === module) {
// command line invocation
startup(function(err, address) {
logger.info(util.format("running on http://%s:%s",
address.address, address.port));
});
} else {
exports.startup = startup;
}

Просмотреть файл

@ -1,19 +0,0 @@
{
"basic_auth_realm": "Basic realm=\"Mozilla Corporation - LDAP Login\"",
"browserid_server": "http://127.0.0.1:10002",
"cookie": {
"secret": "YOU MUST CHANGE ME",
"duration": 86400000
},
"default_lang": "en-US",
"debug_lang": "it-CH",
"http_port": 3000,
"issuer": "dev.clortho.mozilla.org",
"ldap_bind_dn": "mail=USERNAME@mozilla.com,o=com,dc=mozilla",
"ldap_bind_password": "password",
"ldap_server_url": "ldaps://addressbook.mozilla.com:636",
"locale_directory": "locale",
"signin_method": "basicauth",
"supported_languages": ["en-US"],
"use_https": true
}

Просмотреть файл

@ -1,328 +0,0 @@
// vim: shiftwidth=2
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const ldap = require('ldapjs'),
config = require('./configuration'),
logger = require('./logging').logger,
statsd = require('../lib/statsd'),
util = require('util'),
_ = require('underscore'),
fs = require('fs');
// check required configuration at startup
[ 'ldap_server_url', 'ldap_bind_dn', 'ldap_bind_password' ].forEach(function(k) {
if (!config.has(k)) {
logger.error(util.format("Configuration error, you must specifiy '%s'", k));
process.exit(1);
}
});
// create and connect an LDAP client, populate the options block
function createClient(opts, cb) {
var connectStartTime = new Date();
if (typeof opts !== 'object' || opts === null) throw new Error('invalid options parameter');
opts.url = opts.url || config.get('ldap_server_url');
opts.errorCallback = opts.errorCallback || function(err) {
logger.warn(util.format('LDAP connection ended with unhandled error: %s', err));
};
cb = _.once(cb);
var client = ldap.createClient({
url: opts.url,
connectTimeout: opts.connectTimeout || config.get('ldap_server_connect_timeout')
});
var connected = false;
client.on('close', function(err) {
if (!connected) {
err = "connect failed";
}
if (err) {
if (opts.errorCallback) opts.errorCallback(err);
opts.errorCallback = null;
}
});
client.on('error', function(err) {
if (opts.errorCallback) {
opts.errorCallback(err);
}
opts.errorCallback = null;
});
client.on('connect', function() {
statsd.timing('ldap.timing.connect', new Date() - connectStartTime);
connected = true;
cb(null, client);
});
}
function checkOpts(required, got) {
if (typeof got !== 'object' || got === null) {
throw new Error("missing options object");
}
got = Object.keys(got);
var missing = _.difference(required, got);
if (missing.length) {
throw new Error("missing required parameters: " + missing.join(', '));
}
}
/* check if we can authenticate to an ldap server. arguments include:
* url: url to LDAP server
* dn: LDAP distinguished name to bind
* pass: credentials associated with DN
*/
exports.checkBindAuth = function(opts, cb) {
opts.dn = opts.dn || config.get('ldap_bind_dn');
opts.pass = opts.pass || config.get('ldap_bind_password');
return exports.authUser(opts, cb);
};
// fetches the user's LDAP entry and returns an
// object with mail, zimbraAlias and employeeType attributes
function getUserData(opts, cb) {
// required parameters
checkOpts([ 'email', 'dn', 'pass' ], opts);
// 'boundClient' is an optional parameter. When supplied,
// we will use an existing bound LDAP connection rather than
// opening a new one.
// determine the search bases
var domain = opts.email.split('@')[1];
var searchBases = config.get('ldap_search_bases')[domain];
if (!searchBases) {
process.nextTick(function() {
cb(util.format("unsupported domain: %s", domain));
});
return;
}
function withClient(client) {
/**
* This hairy bit of code allows us to search multiple
* base levels of the ldap directory. This exists for several
* reasons:
*
* a) we want to be able to use our mock LDAP server to test with
*
* b) our mock ldap server (ldapjs) does not support extensible
* filtering, otherwise we could search with this filter:
*
* (&(|(mail='+mail+')(zimbraAlias='+mail+'))(|(o:dn:=org)(o:dn:=com)))
*
* and only need one request to the server
*
* c) it is worth the tradeoff(?) of multiple searches, more latency,
* more bandwidth and more complex code to have and easily
* testable code base.
*
* d) it's compatible with Active Directory now ...
*
*/
function searchForEmail(searchBase, mail, searchCallback) {
// no more bases left to search
if (!searchBase) return searchCallback(null, []);
client.search(searchBase, {
scope: 'sub',
filter: '(|(mail='+opts.email+')(zimbraAlias='+opts.email+'))',
attributes: ['mail', 'zimbraAlias', 'employeeType', 'pwdChangedTime']
}, function(err, res) {
var results = [];
if (err) {
logger.warn('error during LDAP search' + err.toString());
return searchCallback(err, false);
}
res.on('searchEntry', function(entry) {
results.push(entry.object);
});
res.on('end', function() {
if (results.length === 0) {
searchForEmail(searchBases.shift(), opts.email, searchCallback);
} else {
searchCallback(null, results);
}
});
});
}
// search searching...
searchForEmail(searchBases.shift(), opts.email, cb);
}
if (opts.boundClient) {
withClient(opts.boundClient);
} else {
createClient(opts, function(err, client) {
// ensure unbind() is called.
cb = _.compose(function() {
client.unbind();
}, cb);
client.bind(opts.dn, opts.pass, function(err) {
if (err) {
logger.warn("Could not bind to get user data");
return cb(err, false);
}
withClient(client);
});
});
}
}
// convert a (canonical) email address to a DN
exports.convertEmailToDN = function(email) {
// is this a supported domain?
var searchBases = config.get('ldap_search_bases');
var domain = email.split('@')[1];
if (!searchBases[domain]) {
throw new Error(util.format("unsupported domain: %s", domain));
}
return util.format("mail=%s,%s", email, searchBases[domain][0]);
};
// given an object, add default .dn and .pass if they do not exist
function addDefaultCredentials(opts) {
if (!opts.dn) {
opts.dn = config.get('ldap_bind_dn');
if (opts.pass) throw new Error("providing a password without a DN is meaningless");
opts.pass = config.get('ldap_bind_password');
}
}
// given an email, map it to a canonical address
exports.canonicalAddress = function(opts, cb) {
addDefaultCredentials(opts);
checkOpts([ 'email', 'dn', 'pass' ], opts);
getUserData(opts, function(err, results) {
if (err) return cb(err, false);
if (results.length !== 0) {
cb(null, results[0].mail);
} else {
err = "Could not find user: " + opts.email;
logger.warn(err);
cb(err, false);
}
});
};
exports.authUser = function(opts, cb) {
// opts.email - a user email to authenticate as
// opts.dn - the dn to authenticate as
// ensure cb is called only once
cb = _.once(cb);
// if email is provided, we assume it is the canonical ldap account and
// convert it into a bind dn
if (opts.email) {
if (opts.dn) throw new Error(".dn and .email are mutually exclusive");
try {
opts.dn = exports.convertEmailToDN(opts.email);
} catch(e) {
process.nextTick(function() { cb(e); });
return;
}
}
checkOpts([ 'dn', 'pass' ], opts);
opts.errorCallback = function(err) {
cb(err);
};
var authStartTime = new Date();
// 1. connect to LDAP server
createClient(opts, function(err, client) {
if (err) return cb(err);
// ensure unbind() is called.
cb = _.compose(function() {
client.unbind();
}, cb);
// 2. bind as target user
client.bind(opts.dn, opts.pass, function(err) {
statsd.timing('ldap.timing.auth', new Date() - authStartTime);
if (err) {
statsd.increment('ldap.auth.wrong_password');
logger.warn('Wrong credentials for user', opts.dn, err);
cb(err, false);
} else {
statsd.increment('ldap.auth.success');
/* only fetch extra info. if we are searching by email.
* this happens only when a user is signing on.
* I should also note, doing this because getUserData
* only takes an email address to locate the *right*
* record in our LDAP directory
*/
if (opts.email) {
// fetch some info about the user
getUserData(opts, function(err, results) {
if (err) {
statsd.increment('ldap.auth.fetch_data_error');
logger.warn("Could not fetch data for user", opts.dn, err);
cb(err, false);
return;
}
cb(null, {
email: opts.email,
zimbraAlias: results[0].zimbraAlias || "",
pwdChangedTime: results[0].pwdChangedTime || ""
});
});
} else {
cb(null, {});
}
}
});
});
};
exports.userMayUseEmail = function(opts, cb) {
// opts.user - canonical user
// opts.email - possibly an alias
addDefaultCredentials(opts);
checkOpts([ 'user', 'email', 'dn', 'pass' ], opts);
getUserData(opts, function(err, results) {
if (err) return cb(err);
if (results.length === 0) return cb("User not found or disabled");
if (results[0].mail !== opts.user) {
return cb(util.format("%s does not own not %s", opts.user, opts.email));
}
if (results[0].employeetype === "DISABLED") {
return cb(util.format("%s account is disabled"), opts.user);
}
return cb(null, {
email: opts.email,
zimbraAlias: results[0].zimbraAlias || "",
pwdChangedTime: results[0].pwdChangedTime || ""
});
});
};

Просмотреть файл

@ -1,132 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const convict = require('convict'),
fs = require('fs'),
path = require('path');
var conf = module.exports = convict({
browserid_server: { format: "url", default: "https://login.persona.org", },
// configuration for "cef" logging, which is used to inject application level
// security events into syslog
security_logging: {
vendor: { format: 'string', default: "Mozilla" },
product: { format: 'string', default: "mozilla-idp" },
version: { format: 'string', default: "0.0.0" },
syslog_tag: { format: 'string', default: "mozilla-idp" },
syslog_host: {
doc: 'Host where syslog service is listening',
format: 'string',
default: "127.0.0.1",
env: 'CEF_SYSLOG_HOST'
},
syslog_port: {
doc: 'Port on which syslog service will receive UDP messages',
format: 'integer',
default: 514,
env: 'CEF_SYSLOG_PORT'
}
},
certificate_validity_s: {
doc: 'the amount of time certificates are valid',
format: 'int',
default: (5 * 60) // five minute default certification validity.
},
cookie: {
secret: { format: 'string', default: "YOU MUST CHANGE ME" },
duration_ms: { format: 'int', default: (24 * 60 * 60 * 1000) }
},
default_lang: { format: 'string', default: 'en-US' },
debug_lang: { format: 'string', default: "it-CH" },
domain_mapping: {
doc: "Testing feature: Allows users to type in a testing domain to trigger the Mozilla IdP, but have their emails rewritten to mozilla domains",
format: Object,
default: {
"mozilla.personatest.org": "mozilla.com"
},
},
http_port: { format: 'int', env: "PORT", default: 3000 },
http_address: { format: 'string', env: "ADDRESS", default: '127.0.0.1' },
issuer: { format: 'string', default: "mozilla.personatest.org" },
ldap_bind_dn: { format: 'string', default: "mail=USERNAME@mozilla.com,o=com,dc=mozilla" },
ldap_bind_password: { format: 'string', default: "password" },
ldap_server_url: {
format: 'string',
default: "ldaps://ldap.mozilla.org:636",
env: 'LDAP_SERVER_URL'
},
ldap_search_bases: {
doc: "The search bases for supported domains. Both restricts the domains we support and provides configurable LDAP search base strings",
format: Object,
default: {
"mozillafoundation.org": [ "o=org,dc=mozilla", "o=com,dc=mozilla" ],
"mozilla.com": [ "o=com,dc=mozilla", "o=org,dc=mozilla" ]
}
},
ldap_server_connect_timeout: { format: 'int', default: 10000 },
locale_directory: { format: 'string', default: "locale" },
statsd: {
host: { format: "string", default: "127.0.0.1" },
port: { format: "int", default: 8125 }
},
supported_languages: {
doc: "List of languages this deployment should detect and display localized strings.",
format: Array,
default: [ "en-US" ],
env: 'SUPPORTED_LANGUAGES'
},
config_path: {
doc: "The path where deployment specific resources, such as keys, will be sought.",
format: 'string',
env: 'CONFIG_PATH',
default: ""
},
auth_lockout_ms: {
doc: "The amount of time to lockout a user upon successive fail auth attemtps",
format: 'int',
default: (5 * 60 * 1000)
},
auth_lockout_attempts: {
doc: "The number of failed authentcation attempts before a user will be locked out",
format: 'int',
default: 5
},
local_development: {
doc: "Run in local development mode, disables secure cookies",
format: 'boolean',
default: false,
env: 'LOCAL_DEV'
}
});
var dev_config_path = path.join(__dirname, '..', 'config', 'local.json');
if (! process.env.CONFIG_FILES &&
fs.existsSync(dev_config_path)) {
process.env.CONFIG_FILES = dev_config_path;
}
// handle configuration files. you can specify a CSV list of configuration
// files to process, which will be overlayed in order, in the CONFIG_FILES
// environment variable
if (process.env.CONFIG_FILES) {
var files = process.env.CONFIG_FILES.split(',');
files.forEach(function(file) {
var c = JSON.parse(fs.readFileSync(file, 'utf8'));
conf.load(c);
});
}
// if var path has not been set, let's default to var/
// XXX: due to a bug in convict, .has() seems to not be working properly,
// thus we must explicitly check for the empty string
if (conf.get('config_path') === "") {
conf.set('config_path', path.join(__dirname, "..", "config"));
}
// massage bind address to something node will understand
if ([ '0.0.0.0', '*' ].indexOf(conf.get('http_address')) !== -1) {
conf.set('http_address', null);
}

Просмотреть файл

@ -1,52 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const jwcrypto = require("jwcrypto"),
fs = require("fs"),
assert = require("assert"),
cert = jwcrypto.cert,
config = require('./configuration');
// load desired algorithms
require("jwcrypto/lib/algs/rs");
require("jwcrypto/lib/algs/ds");
// TODO move these to a shared constants/config file eventually
// and share it with scripts/gen_keys.js
var configDir = fs.realpathSync(config.get('config_path'));
var pubKeyFile = configDir + "/public-key.json";
var secretKeyFile = configDir + "/secret-key.json";
// Load Pub/Private keys from the filesystem, exit loudly if you can't read them
var missingFileErr = null;
if (!fs.existsSync(pubKeyFile)) missingFileErr = "Public Key file ["+ pubKeyFile + "] does not exist";
if (!fs.existsSync(secretKeyFile)) missingFileErr = "Secret Key file ["+secretKeyFile+"] does not exist";
if (missingFileErr) {
console.error('ERROR:', missingFileErr);
console.log("\n---> run scripts/gen_keys.js to fix this\n");
console.log();
process.exit(1);
}
var _privKey = jwcrypto.loadSecretKey(fs.readFileSync(secretKeyFile));
exports.pubKey = fs.readFileSync(pubKeyFile);
exports.cert_key = function(pubkey, email, duration_s, cb) {
var pubKey = jwcrypto.loadPublicKey(pubkey);
var expiration = new Date();
var iat = new Date();
expiration.setTime(new Date().valueOf() + (duration_s * 1000));
// Set issuedAt to 10 seconds ago. Pads for verifier clock skew
iat.setTime(iat.valueOf() - (10 * 1000));
cert.sign(
{publicKey: pubKey, principal: {email: email}},
{issuer: config.get('issuer'), issuedAt: iat, expiresAt: expiration},
null,
_privKey,
cb);
};

Просмотреть файл

@ -1,14 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const config = require('./configuration.js');
module.exports = function(email) {
var domainMapping = config.get('domain_mapping');
var parts = email.split('@');
if (parts.length === 2 && domainMapping[parts[1]]) {
email = parts[0] + '@' + domainMapping[parts[1]];
}
return email;
};

Просмотреть файл

@ -1,155 +0,0 @@
/*
* Mock LDAP server, useful for testing and development
*
*/
const ldap = require("ldapjs");
module.exports = function() {
var i = 0;
// mmmmm... all passwords are "testtest"
var directory = [
// the vinz clortho binds as this user
{dn: "cn=vinz, o=com, dc=mozilla", attributes: { cn: "vinz", password: "testtest" }},
// for testing of actual live hosts
{dn: "mail=user@clortho.personatest.org, o=com, dc=mozilla",
attributes: { mail: "user@clortho.personatest.org" }}
];
// Create some Testing users
for(i=1; i <= 6; i++) {
directory.push({
dn: "mail=user"+i+"@mozilla.com, o=com, dc=mozilla",
attributes: {
// note only use lowercase attribute names, seems to be an ldapjs
// implementationd detail
mail: "user"+i+"@mozilla.com",
zimbraalias: 'alias'+i+"@mozilla.com",
password:"testtest",
employeetype: "Tester",
pwdChangedTime: "12345Z"
}
});
}
for(i=1; i <= 6; i++) {
directory.push({
dn: "mail=user"+i+"@mozillafoundation.org, o=org, dc=mozilla",
attributes: {
mail: "user"+i+"@mozillafoundation.org",
zimbraalias: 'alias'+i+"@mozillafoundation.org",
password: "testtest",
employeetype: "Tester",
pwdChangedTime: "12345Z"
}
});
}
// add a .org mail under o=com
directory.push({
dn: "mail=user_a@mozilla.com, o=com, dc=mozilla",
attributes: {
// note only use lowercase attribute names, seems to be an ldapjs
// implementationd detail
mail: "user_a@mozilla.com",
zimbraalias: 'test_a@mozillafoundation.org',
password:"testtest",
employeetype: "Tester",
pwdChangedTime: "12345Z"
}
});
// add a .com under o=org
directory.push({
dn: "mail=user_a@mozillafoundation.org, o=org, dc=mozilla",
attributes: {
// note only use lowercase attribute names, seems to be an ldapjs
// implementationd detail
mail: "user_a@mozillafoundation.org",
zimbraalias: 'test_a@mozilla.com',
password:"testtest",
employeetype: "Tester",
pwdChangedTime: "12345Z"
}
});
function bindHandler(req, res, next) {
var bindDN = req.dn.toString();
var credentials = req.credentials;
for(var i=0; i < directory.length; i++) {
if(directory[i].dn === bindDN &&
credentials === directory[i].attributes.password &&
directory[i].attributes.employeetype !== 'DISABLED') {
this.emit('bind', {
success: true,
dn: bindDN,
credentials: credentials
});
res.end();
return next();
}
}
this.emit('bind', {
success: false,
dn: bindDN,
credentials: credentials
});
return next(new ldap.InvalidCredentialsError());
}
function searchHandler(req, res, next) {
directory.forEach(function(user) {
// this test is pretty dumb, make sure in the directory
// that things are spaced / cased exactly
if (user.dn.indexOf(req.dn.toString()) === -1) {
return;
}
if (req.filter.matches(user.attributes)) {
res.send(user);
}
});
res.end();
return next();
}
// some middleware to make sure the user has a successfully bind
function authorize(req, res, next) {
for(var i=0; i < directory.length; i++) {
if (req.connection.ldap.bindDN.equals(directory[i].dn)) {
this.emit('authorize', {
success: true,
dn: req.connection.ldap.bindDN
});
return next();
}
}
this.emit('authorize', {
success: false,
dn: req.connection.ldap.bindDN
});
return next(new ldap.InsufficientAccessRightsError());
}
var ldapServer = ldap.createServer();
ldapServer.bind('dc=mozilla', bindHandler);
ldapServer.search('dc=mozilla', [authorize], searchHandler);
return {
directory: directory,
server: ldapServer,
bindHandler: bindHandler,
searchHandler: searchHandler
};
};

Просмотреть файл

@ -1,14 +0,0 @@
/* a tiny wrapper around winston that let's us route all logging in the
* application through winston, and at a later point
* do more complex things with logging configuration per environment if
* needed */
// simply export winston
exports.logger = require('winston');
// enable logging of uncaught exceptions
exports.logger.handleExceptions(new exports.logger.transports.Console());
exports.disable = function() {
exports.logger.remove(exports.logger.transports.Console);
};

Просмотреть файл

@ -1,22 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var useragent = require('useragent'),
policy = 'CP="This is not a P3P policy, but Mozilla deeply cares about ' +
'your privacy. See http://www.mozilla.org/persona/privacy-policy ' +
'for more."';
// add 'P3P' headers so that IE8, with default security settings, will allow
// us to set third-party cookies. Only add the headers in that case, saving
// bytes for all the other browsers. #2340
module.exports = function(req, res, next) {
var browser = useragent.parse(req.headers['user-agent']);
if (browser.family === 'IE') {
res.on('header', function() {
res.setHeader('P3P', policy);
});
}
next();
};

Просмотреть файл

@ -1,21 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Security logging via 'CEF' is intended to report security events
* that could not be caused by non-malicious usage of the system.
*
* Some basic guidance with respect to "what to log" are here:
* - https://wiki.mozilla.org/Security/Users_and_Logs#What_to_Log
*
* With respect to the Mozilla IdP, event to log will be of the
* nature - 5 password failures in a row.
*/
var cef = require('cef'),
conf = require('./configuration.js');
module.exports = new cef.Logger(conf.get('security_logging'));

Просмотреть файл

@ -1,23 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const
StatsD = require("node-statsd").StatsD,
config = require('./configuration'),
logger = require('./logging').logger;
// report statistics with a "mozillaidp" prefix.
const PREFIX = "mozillaidp.";
var statsd = new StatsD(config.get('statsd.host'), config.get('statsd.port'));
// start by exporting a stubbed no-op stats reporter
module.exports = {
timing: function(s, v) {
if (statsd) { statsd.timing(PREFIX + s, v); }
},
increment: function(s, v) {
if (statsd) { statsd.increment(PREFIX + s, v); }
}
};

Просмотреть файл

@ -1,54 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* A simple in-memory throttle for failed authentication. This being
* in memory means a couple things:
* * For multiple server deployments, authenticationt throttling
* counts are node-local
* * client code should not add failed authentication attempts
* for non-existant users, lest a remote attacker can fatally bloat
* process memory
*/
var config = require('./configuration');
/* keep track of failed authentication attempt. Keys are email addresses,
* values include `time` (time of last failure) and `count` (the number
* of consecutive failures */
var fails = {};
exports.check = function(email, cb) {
if (fails[email]) {
if ((new Date() - fails[email].time) > config.get('auth_lockout_ms')) {
delete fails[email];
}
}
if (!cb) return;
process.nextTick(function() {
if (fails[email] &&
fails[email].count >= config.get('auth_lockout_attempts')) {
cb("lockout");
} else {
cb(null);
}
});
};
exports.failed = function(email, cb) {
if (!fails[email]) {
fails[email] = { count: 0 };
}
fails[email].count++;
fails[email].time = new Date();
if (cb) process.nextTick(cb);
};
exports.clear = function(email, cb) {
if (fails[email]) delete fails[email];
if (cb) process.nextTick(cb);
};

Просмотреть файл

@ -1,145 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// a teensy tinsy module to do parameter validation. A good candiate for future
// librification.
//
// usage:
//
// const validate = require('validate.js');
//
// app.post('/wsapi/foo', validate({
// email: 'email',
// site: "origin"
// }), function(req, resp) {
// // handler that uses .params
// });
const
logger = require('./logging.js').logger,
check = require('validator').check;
var hostnameRegex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
var types = {
email: function(x) {
check(x).isEmail();
},
email_type: function(x) {
check(x).isIn([ 'primary', 'secondary' ]);
},
password: function(x) {
check(x).len(6,80);
},
boolean: function(x) {
if (typeof x !== 'boolean') throw "boolean required";
},
token: function(x) {
check(x).len(48,48).isAlphanumeric();
},
assertion: function(x) {
check(x).len(50,10240).regex(/[0-9a-zA-Z~_\-]+/);
},
pubkey: function(x) {
check(x).len(50,10240);
JSON.parse(x);
},
hostname: function(x) {
check(x).is(hostnameRegex);
},
origin: function(x) {
/* origin regex
/^ // beginning
https?:\/\/ // starts with http:// or https://
(?=.{1,254}(?::|$)) // hostname must be within 1-254 characters
(?: // match hostname part (<part>.<part>...)
(?!-) // cannot start with a dash (allow it to start with a digit re issue #2042)
(?![a-z0-9\-]{1,62}- // part cannot end with a dash
(?:\.|:|$)) // (end of part will be '.', ':', or end of str)
[a-z0-9\-]{1,63}\b // part will be 1-63 letters, numbers, or dashes
(?!\.$) // final part cannot end with a '.'
\.? // part followed by '.' unless final part
)+ // one or more hostname parts
(:\d+)? // optional port
$/i; // end; case-insensitive
*/
var regex = /^https?:\/\/(?=.{1,254}(?::|$))(?:(?!-)(?![a-z0-9\-]{1,62}-(?:\.|:|$))[a-z0-9\-]{1,63}\b(?!\.$)\.?)+(:\d+)?$/i;
if (typeof x !== 'string' || !x.match(regex)) {
throw new Error("not a valid origin");
}
}
};
module.exports = function (params) {
// normalize the parameters description, verify all specified types are present
if (Array.isArray(params) || typeof params !== 'object' || typeof params === null) {
throw "argument to validate must be an object, not a " + (typeof params);
}
Object.keys(params).forEach(function(p) {
var v = params[p];
if (typeof v === 'string') {
v = { type: v };
}
if (typeof v.required === "undefined") v.required = true;
if (!types[v.type]) throw "unknown type specified in WSAPI:" + v.type;
params[p] = v;
});
return function(req, resp, next) {
var reqParams = null;
if (req.method === "POST") {
reqParams = req.body;
} else {
reqParams = req.query;
}
// clear body and query to prevent wsapi handlers from accessing
// un-validated input parameters
req.body = {};
req.query = {};
req.params = {};
// now validate
try {
// allow csrf through
if (reqParams.csrf) {
req.params.csrf = reqParams.csrf;
delete reqParams.csrf;
}
Object.keys(params).forEach(function(p) {
if (params[p].required && !reqParams.hasOwnProperty(p)) throw "missing required parameter: '" + p + "'";
if (reqParams[p] === undefined) return;
// validate
try {
types[params[p].type](reqParams[p]);
} catch (e) {
throw p + ": " + e.toString();
}
req.params[p] = reqParams[p];
delete reqParams[p];
});
// if there are any keys left in reqParams, they're not allowable!
var extra = Object.keys(reqParams);
if (extra.length) throw "unsupported parameter: '" + extra.join("', '") + "'";
} catch(e) {
var msg = {
success: false,
reason: e.toString()
};
logger.warn("bad request received: " + msg.reason);
resp.statusCode = 400;
return resp.json(msg);
}
// this is called outside the try/catch because errors
// in the handling of the request should be caught separately
next();
};
};

Просмотреть файл

@ -1,240 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const config = require('./lib/configuration'),
crypto = require('./lib/crypto'),
util = require('util'),
emailRewrite = require('./lib/email_rewrite.js'),
auth = require('./lib/auth'),
ldap = require('ldapjs'),
logger = require('./lib/logging.js').logger,
statsd = require('./lib/statsd'),
throttle = require('./lib/throttle'),
secLog = require('./lib/security_logging'),
url = require('url');
// apply X-Content-Security-Policy headers to HTML resources served
function applyContentSecurityPolicy(res) {
['X-Content-Security-Policy','Content-Security-Policy'].forEach(function(header) {
res.setHeader(header,
util.format("default-src 'self' %s",
config.get('browserid_server')));
});
}
exports.routes = function () {
return {
well_known_browserid: function (req, resp) {
var pk = crypto.pubKey;
resp.setHeader('Content-Type', 'application/json');
resp.setHeader('Cache-Control', 'max-age=5, public');
resp.render('well_known_browserid', {
public_key: pk,
layout: false
});
},
provision: function (req, resp) {
statsd.increment('provision.attempt');
applyContentSecurityPolicy(resp);
// This cookie tests 3rd party Cookie blocking
req.session.question = "Are you the Keymaster?";
resp.render('provision', {
user: req.session.email,
browserid_server: config.get('browserid_server'),
layout: false
});
},
signin: function (req, resp) {
if (! req.session.question) {
statsd.increment('auth.3rd_party_cookies_fail');
return resp.render('3rd_party_cookies',
{ title: req.gettext("Sign In") });
}
statsd.increment('auth.attempt');
var email = (req.query ? req.query.email : null);
if (email) email = emailRewrite(email);
// prevent framing of authentication page.
resp.setHeader('X-Frame-Options', 'DENY');
applyContentSecurityPolicy(resp);
resp.render('signin', {
title: req.gettext("Sign In"),
email: email
});
},
welcome: function (req, resp) {
resp.setHeader('X-Frame-Options', 'DENY');
applyContentSecurityPolicy(resp);
resp.render('welcome', {
title: req.gettext("The Mozilla Identity Provider")
});
},
handle404: function (req, resp) {
resp.setHeader('X-Frame-Options', 'DENY');
applyContentSecurityPolicy(resp);
resp.render('404', {
title: req.gettext("No Content Found"),
status: 404
});
},
// API end points
provision_key: function (req, resp) {
// check that there is an authenticated user
if (!req.session || !req.session.email) {
return resp.send('No Session', 401);
}
// check that required arguments are supplied
if (!req.params.pubkey || !req.params.user) {
return resp.send(400);
}
// check that the user is authenticated as the target user
auth.userMayUseEmail({
user: req.session.email,
email: emailRewrite(req.params.user).toLowerCase()
}, function(err, userData) {
if (err) {
logger.warn("cannot provision user:", err);
statsd.increment('provision.failure');
return resp.send(401);
}
// if the user has changed their password since the last
// provision then force them to log in again
if (userData.pwdChangedTime !== req.session.pwdChangedTime) {
statsd.increment('provision.pwdChangedTime mismatch');
req.session.reset();
return resp.send('Password Changed. Reauthentication required.', 401);
}
crypto.cert_key(
req.params.pubkey,
req.params.user,
config.get('certificate_validity_s'),
function(err, cert) {
if (err) {
resp.writeHead(500);
resp.end();
} else {
// successful provisioning
statsd.increment('provision.success');
resp.json({ cert: cert });
}
});
});
},
check_signin: function (req, resp) {
var mozillaUser = "";
if (req.params.user) {
mozillaUser = emailRewrite(req.params.user).toLowerCase();
}
if (!req.params.user || !req.params.pass) {
resp.writeHead(400);
return resp.end();
} else {
auth.canonicalAddress({ email: mozillaUser }, function(err, mozillaUser) {
if (err) {
resp.json({
success: false,
reason: 'email not found'
}, 401);
return;
}
throttle.check(mozillaUser, function(err) {
if (err) {
// Send an event to the security log for every authentication
// attempt to a throttle account.
secLog.warn({
signature: 'AUTH_LOCKOUT',
name: "attempted login to a throttled account",
extensions: {
suser: mozillaUser
}
});
statsd.increment('auth.throttle');
// as per security guidelines, account throttling should
// be indistiguishable from wrong password. This is a
// usability loss in the name of security
// https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines
resp.json({
success: false,
reason: 'email or password incorrect'
}, 401);
return;
}
auth.authUser({
email: mozillaUser,
pass: req.params.pass
}, function (err, userData) {
if (err || userData === false) {
// if this is a password failure, note it in our password
// throttling
if (err && err.name === 'InvalidCredentialsError') {
throttle.failed(mozillaUser);
}
resp.json({
success: false,
reason: 'email or password incorrect'
}, 401);
} else {
// upon successful authentication, clear any throttling
// for this user
throttle.clear(mozillaUser);
req.session.email = mozillaUser;
req.session.pwdChangedTime = userData.pwdChangedTime;
resp.send({ success: true }, 200);
statsd.increment('auth.success');
}
});
});
});
}
},
session_context: function(req, res) {
res.json({
csrf: req.session._csrf
}, 200);
},
// Monitoring End points
// the ELB (elastic load balancer) check is just to make
// sure that node returns a response
elb_check: function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.send("OK");
},
// checks that we can bind against the LDAP server
// this check is for our global load balancers so they
// can add / remove regions if LDAP connectivity drops
checkStatus: function(req, res) {
auth.checkBindAuth({}, function(err) {
res.setHeader('Content-Type', 'text/plain');
if (err) {
statsd.increment('healthcheck.error');
// try message, no? has name? no ... "unknown"
var output = "Error: " + err.name;
res.send(output, 503);
} else {
statsd.increment('healthcheck.ok');
res.send('OK');
}
});
},
// QA Only URLs
signout: function (req, resp) { req.session.reset(); resp.redirect('/'); }
};
};

Просмотреть файл

@ -1,12 +0,0 @@
<div class="title">
<img src="/i/mozilla-wordmark-200.png" alt="<%= gettext("Mozilla Logo") %>" />
</div>
<div class="signin-panel">
<h1><%= gettext("Error:") %></h1>
<p><%= gettext("login.mozilla.org cookies blocked") %></p>
<p class="error"><%- format(gettext('You need third-party cookies enabled. <a href="%s">Read More</a>.'),
["https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences?esab=a&s=3rd+party+cookies&r=2&as=s"]) %></p>
</div><!-- #sign-in-panel -->

Просмотреть файл

@ -1,8 +0,0 @@
<div class="title">
<img src="/i/mozilla-wordmark-200.png" alt="<%= gettext("Mozilla Logo") %>" />
</div>
<div class="signin-panel">
<div class="prompt">
<p><%= gettext("No content found at this URL! So Sorry.") %></p>
</div>
</div>

Просмотреть файл

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="<%= lang %>" dir="<%= lang_dir %>">
<head>
<meta charset="utf-8">
<title><%= title %></title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="/css/stylo.css">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="/opensans-regular,opensans-light/fonts.css" type="text/css" rel="stylesheet"/ >
</head>
<body>
<%- body %>
<script src="<%= browserid_server %>/authentication_api.js"></script>
<script src="/js/lib/jquery.min.js"></script>
<script src="/js/signin.js"></script>
</body>
</html>

Просмотреть файл

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>provisioning</title>
</head>
<body>
<input type="hidden" name="_csrf" value="<%= csrf_token %>" />
<script type="text/javascript" src="<%= browserid_server %>/provisioning_api.js"></script>
<script type="text/javascript" src="/js/lib/jquery.min.js"></script>
<script type="text/javascript" src="/js/provision.js" /></script>
</body>
</html>

Просмотреть файл

@ -1,35 +0,0 @@
<div class="title">
<img src="/i/mozilla-wordmark-200.png" alt="<%= gettext("Mozilla Logo") %>" />
</div>
<div class="signin-panel">
<div class="prompt">
<p><%= gettext("Please use your LDAP password") %></p>
</div>
<form action="/api/sign_in" method="POST" novalidate>
<input type="hidden" name="_csrf" value="<%= csrf_token %>" />
<div class="center-col">
<label class="hidden" for="user"><%= gettext("Email address") %></label>
<input id="user" type="email" name="user" value="<%= email %>"
autocorrect="off" autocapitalize="off" />
</div>
<div class="center-col">
<label class="hidden" for="pass"><%= gettext("password") %></label>
<input type="password" id="pass" name="pass" value="" autofocus />
</div>
<div class="center-col last clearfix">
<button><%= gettext('sign in') %></button>
<button class="cancel"><%= gettext('cancel') %></button>
</div>
</form>
</div><!-- #sign-in-panel -->
<div class="tooltip">
<span class="contents"></span>
<div class="arrow-down"></div>
</div>

Просмотреть файл

@ -1,11 +0,0 @@
<div class="title">
<img src="/i/mozilla-wordmark-200.png" alt="<%= gettext("Mozilla Logo") %>" />
</div>
<div class="overview">
<p><strong><%= gettext("The Mozilla Identity Provider") %></strong>:
<em><%= gettext("Better LDAP authentication for Mozilla employees since 2013.") %></em>
</p>
<p>
<%- gettext('To use this, log in with your Mozilla email address anywhere <a href="https://persona.org">Persona</a> is supported.') %>
</p>
</div>

Просмотреть файл

@ -1,5 +0,0 @@
{
"public-key": <%- public_key %>,
"authentication": "/sign_in",
"provisioning": "/provision"
}

Просмотреть файл

@ -1,189 +0,0 @@
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
tt,
var,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
outline: 0;
vertical-align: baseline;
background: transparent;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
position: relative;
}
.outer {
float: left;
clear: both;
position: relative;
left: 50%;
}
.inner {
position: relative;
left: -50%;
}
html {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
font-family: helvetica, arial, verdana, sans-serif;
text-rendering: optimizeLegibility;
}
.floatClear {
float: left;
clear: both;
}
img {
border: 0;
}
ul {
list-style-type: none;
}
.clearfix {
zoom: 1;
}
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
#p404 {
color: #FFF;
background-color: #021D48;
line-height: 1.5;
font-family: Times;
}
#p404 div {
width: 1000px;
margin: auto;
margin-top: 15%;
border: solid 1px white;
}
#p404 div img {
width: 573px;
float: left;
}
#p404 div div {
display: table-cell;
vertical-align: middle;
width: 398px;
margin: 0 auto;
margin-left: 537px;
height: 300px;
border: none;
vertical-align: bottom;
text-align: center;
}
#p404 div div blockquote {
width: 358px;
display: block;
font-size: 32px;
margin: 10 auto;
}
#p404 div div blockquote em {
font-style: italic;
}
#p404 div div blockquote,
#p404 div div p {
padding-left: 38px;
}
#p404 div div p {
margin-top: 10px;
}
#p404 div .clear {
clear: both;
}

Просмотреть файл

@ -1,552 +0,0 @@
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
tt,
var,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
outline: 0;
vertical-align: baseline;
background: transparent;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
.outer {
float: left;
clear: both;
position: relative;
left: 50%;
}
.inner {
position: relative;
left: -50%;
}
body {
font-family: 'Open Sans', 'sans-serif', 'serif';
text-rendering: optimizeLegibility;
line-height: 1.5;
position: relative;
}
.floatClear {
float: left;
clear: both;
}
img {
border: 0;
}
ul {
list-style-type: none;
}
.clearfix {
zoom: 1;
}
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
/* === Layout === */
body {
line-height: 1.5;
}
div.title {
width: 200px;
margin: 0 auto;
}
.signin-panel {
border: solid 0px #ff0000;
width: 325px;
margin: 0 auto;
padding: 40px 20px;
}
.left-col {
clear: left;
text-align: right;
}
html[dir=rtl] .left-col {
clear: right;
text-align: left;
}
.left-col {
border: solid 0px #0000ff;
min-width: 113px;
max-width: 173px;
min-height: 1px;
overflow: hidden;
float: left;
}
html[dir=rtl] .left-col,
html[dir=rtl] .right-col,
html[dir=rtl] .center-col {
float: right;
}
.prompt {
text-align: center;
margin-bottom: 1em;
font-size: 1.1em;
font-weight: 300;
}
.center-col {
width: 200px;
margin: auto;
margin-top: .5em;
}
.center-col.last {
margin-top: 1em;
position: relative;
}
.clear {
clear: both;
}
button {
font-size: .8em;
font-weight: bold;
padding: .3em 1.8em .4em;
margin: 0;
display: inline-block;
-moz-border-radius: .25em;
border-radius: .25em;
box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3);
background-color: #43a6e2;
background-color: #277ac1;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear,left top,left bottom,from(#43a6e2),to(#277ac1));
background-image: -moz-linear-gradient(#43a6e2,#277ac1);
background-image: -ms-linear-gradient(#43a6e2,#277ac1);
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#43a6e2),color-stop(100%,#277ac1));
background-image: -webkit-linear-gradient(#43a6e2,#277ac1);
background-image: -o-linear-gradient(#43a6e2,#277ac1);
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#43a6e2', endColorstr='#277ac1', GradientType=0)";
background-image: linear-gradient(#43a6e2,#277ac1);
color: #fff;
border: 0;
text-shadow: 0 1px 0 rgba(0,0,0,0.25);
cursor: pointer;
float: left;
}
button:hover,
button:focus,
.button:hover,
.button:focus {
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2), 0 2px 0 rgba(0, 0, 0, 0.1);
background-color: #4aafe5;
background-image: -webkit-gradient(linear, left top, left bottom, from(#4aafe5), to(#2c89c8));
background-image: -webkit-linear-gradient(top, #4aafe5, #2c89c8);
background-image: -moz-linear-gradient(top, #4aafe5, #2c89c8);
background-image: -ms-linear-gradient(top, #4aafe5, #2c89c8);
background-image: -o-linear-gradient(top, #4aafe5, #2c89c8);
background-image: linear-gradient(top, #4aafe5, #2c89c8);
}
button:focus,
.button:focus {
box-shadow: 0 0 1px #fff, 0 0 1px 3px #49ADE3;
box-shadow: 0 0 1px rgba(255, 255, 255, 0.5), 0 0 1px 3px rgba(73, 173, 227, 0.6);
}
button:active,
.button:active {
background-color: #184a73;
background-image: -webkit-gradient(linear, left top, left bottom, from(#184a73), to(#276084));
background-image: -webkit-linear-gradient(top, #184a73, #276084);
background-image: -moz-linear-gradient(top, #184a73, #276084);
background-image: -ms-linear-gradient(top, #184a73, #276084);
background-image: -o-linear-gradient(top, #184a73, #276084);
background-image: linear-gradient(top, #184a73, #276084);
color: #97b6ca;
text-shadow: 0 1px rgba(0,0,0,0.4);
box-shadow: inset 0 2px 1px rgba(0,0,0,0.3);
}
button::-moz-focus-inner, .button::-moz-focus-inner {
padding: 0;
border: 0
}
button.cancel {
float: right;
background-color: #d94f30;
background-image: -webkit-gradient(linear, left top, left bottom, from(#d94f30), to(#ad1804));
background-image: -webkit-linear-gradient(top, #d94f30, #ad1804);
background-image: -moz-linear-gradient(top, #d94f30, #ad1804);
background-image: -ms-linear-gradient(top, #d94f30, #ad1804);
background-image: -o-linear-gradient(top, #d94f30, #ad1804);
background-image: linear-gradient(top, #d94f30, #ad1804);
filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#d93f30, endColorstr=#ad1804)";
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#d93f30, endColorstr=#ad1804)";
}
button.cancel:hover,
button.cancel:focus {
background-color: #e3653f;
background-image: -webkit-gradient(linear, left top, left bottom, from(#e3653f), to(#c01c03));
background-image: -webkit-linear-gradient(top, #e3653f, #c01c03);
background-image: -moz-linear-gradient(top, #e3653f, #c01c03);
background-image: -ms-linear-gradient(top, #e3653f, #c01c03);
background-image: -o-linear-gradient(top, #e3653f, #c01c03);
background-image: linear-gradient(top, #e3653f, #c01c03);
}
button.cancel:active,
.button.cancel:active {
box-shadow: 0 0 5px #333 inset;
color: #cfa391;
background-color: #83311e;
background-image: -webkit-gradient(linear, left top, left bottom, from(#83311e), to(#670d01));
background-image: -webkit-linear-gradient(top, #83311e, #670d01);
background-image: -moz-linear-gradient(top, #83311e, #670d01);
background-image: -ms-linear-gradient(top, #83311e, #670d01);
background-image: -o-linear-gradient(top, #83311e, #670d01);
background-image: linear-gradient(top, #83311e, #670d01);
}
/* === Basic Style === */
html {
height: 100%;
}
body {
font-family: 'Open Sans', 'sans-serif', 'serif';
background-image: linear-gradient(bottom, rgb(62,72,79) 39%, rgb(91,104,112) 70%);
background-image: -o-linear-gradient(bottom, rgb(62,72,79) 39%, rgb(91,104,112) 70%);
background-image: -moz-linear-gradient(bottom, rgb(62,72,79) 39%, rgb(91,104,112) 70%);
background-image: -webkit-linear-gradient(bottom, rgb(62,72,79) 39%, rgb(91,104,112) 70%);
background-image: -ms-linear-gradient(bottom, rgb(62,72,79) 39%, rgb(91,104,112) 70%);
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.39, rgb(62,72,79)),
color-stop(0.7, rgb(91,104,112))
);
background-color: #4D4E53;
background-repeat: no-repeat;
overflow-y: auto;
color: #ccc;
}
a {
color: #ddd;
text-decoration: none;
border-bottom:1px dashed;
}
.overview {
max-width: 600px;
margin: auto;
font-size: 1.2em;
text-align: center;
}
.overview p {
margin-top: 1em;
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
.signin-panel {
color: #555;
background-color: #FFF;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
-moz-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25), 3px 3px 60px rgba(0, 0, 0, 0.25), 5px 2px 16px rgba(0, 0, 0, 0.25);
-webkit-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25), 3px 3px 60px rgba(0, 0, 0, 0.25), 5px 2px 16px rgba(0, 0, 0, 0.25);
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25), 3px 3px 60px rgba(0, 0, 0, 0.25), 5px 2px 16px rgba(0, 0, 0, 0.25);
background: #f7f7f7;
background-image: -moz-linear-gradient(top, #ffffff, #ffffff 40%, #eeeeee);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), color-stop(0.4, #ffffff), to(#eeeeee));
background-image: -webkit-linear-gradient(top, from(#ffffff), color-stop(0.4, #ffffff), to(#eeeeee));
background-image: -o-linear-gradient(top, #ffffff 0%, #ffffff 40%, #eeeeee 100%);
filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff, endColorstr=#eeeeee)";
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff, endColorstr=#eeeeee)";
background-image: linear-gradient(top, #ffffff 0%, #ffffff 40%, #eeeeee 100%);
}
label {
font-family: Arial;
}
.hidden {
/* This funkiness is so that labels still appear for screen readers */
height: 0;
visibility: hidden;
display: block;
}
.logo {
padding-top: 10px;
}
/* === Heading === */
h1 {
font-size: 1.5em;
margin-bottom: -0.3em;
}
/* === Labels === */
label {
font-weight: bold;
}
/* === Inputs === */
input {
width: 100%;
border: 1px solid #666;
padding: .4em .5em;
-moz-border-radius: .3em;
border-radius: .3em;
font-size: .8em;
border-color: #b2b2b2;
color: #383838;
}
input:focus {
border: 1px solid #0096dc;
box-shadow: 0 0 5px 0 #0096dc;
}
input[type=password]:focus {
color: #2d79c1;
}
input:disabled {
background-color: #fff;
}
.tooltip {
color: #fff;
font-size: 0.8em;
font-weight: bold;
text-shadow: 0 1px #333;
line-height: 16px;
background-color: #aa1401;
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(218, 81, 50, 0.9)), to(rgba(169, 19, 0, 1)));
background-image: -webkit-linear-gradient(top, rgba(218, 81, 50, 0.9), rgba(169, 19, 0, 1));
background-image: -moz-linear-gradient(top, rgba(218, 81, 50, 0.9), rgba(169, 19, 0, 1));
background-image: -ms-linear-gradient(top, rgba(218, 81, 50, 0.9), rgba(169, 19, 0, 1));
background-image: -o-linear-gradient(top, rgba(218, 81, 50, 0.9), rgba(169, 19, 0, 1));
background-image: linear-gradient(top, rgba(218, 81, 50, 0.9), rgba(169, 19, 0, 1));
border-radius: 4px;
padding: 10px 15px;
box-shadow: 0 1px #666;
display: none;
max-width: 275px;
position: absolute;
top: 10px;
left: 10px;
z-index: 5;
}
.tooltip .arrow-down {
/* This is an awesome CSS only arrow! */
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #aa1401;
border-top: 10px solid rgba(169, 19, 0, 1);
position: absolute;
bottom: -10px;
left: 20px;
}
/* === Hey brother */
@media only screen and (max-width: 345px) {
body {
max-width: 100%;
}
img {
max-width: 100%;
}
.signin-panel {
border: solid 0px #ff0000;
margin: 0 auto;
width: 95%;
padding: 20px;
}
html[dir=rtl] .signin-panel {
margin-left: 1em;
}
.right-col {
width: 1em;
}
.left-col {
clear: auto;
text-align: left;
padding-left: 20px;
}
html[dir=rtl] .left-col {
clear: none;
text-align: right;
padding-right: 20px;
}
.left-col,
.right-col {
border: solid 0px #0000ff;
min-width: 400px;
max-width: 400px;
min-height: 1px;
overflow: hidden;
float: none;
}
html[dir=rtl] .left-col,
html[dir=rtl] .right-col,
html[dir=rtl] .center-col {
float: none;
}
.center-col {
border: auto;
width: 100%;
float: none;
margin-top: 15px;
}
button {
height: 40px;
font-size: 14px;
}
input {
padding: 9px 5px;
}
}
@media only screen and (max-width: 275px) {
/**
* At this width, the buttons start to overlay each other. Put them each on
* their own line and let them expand to the full width.
*/
button, button.cancel {
float: none;
width: 100%;
}
button.cancel {
margin-top: 15px;
}
/**
* Give a little border to make the buttons look more balanced
*/
.center-col.last {
border-top: 1px dashed #ddd;
padding-top: 15px;
}
}
.error {
margin-top: 10px;
}
.error a {
color: #000;
}

Просмотреть файл

@ -1,15 +0,0 @@
PRELIMINARY DNT POLICY
This domain interprets DNT as a request for an opt out of collection and
retention of visitors' reading habits, which we will respect, subject to
reasonable exceptions that respect user privacy.
This is a temporary document. A full DNT Policy is being drafted at
https://eff.org/dnt-policy . When that document is finished, this domain may
decide to adopt it.
You can agree to this same policy by posting it at
https://subdomain.example.com/.well-known/dnt-policy.txt, where "subdomain" is
any domain to which the policy applies. Commonly it will be posted on third
party domains. HTTPS is required.

Двоичные данные
static/i/mozilla-wordmark-200.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.6 KiB

Двоичные данные
static/i/mozilla200x50.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.1 KiB

Двоичные данные
static/i/mozilla200x50_purple.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Двоичные данные
static/i/mozilla200x50_red.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичные данные
static/i/mozilla_corp.jpeg

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 8.4 KiB

Двоичные данные
static/i/vinz_clortho_crop.jpg

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 50 KiB

6
static/js/lib/jquery.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,31 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function () {
// Note: we explicitly ignore persona suggested certificate duration
// and issue short-lived certs. Because re-provisioning is
// user invisible, this allows us a means of remote session
// termination in case of security breaches.
navigator.id.beginProvisioning(function(email /* , cert_duration */) {
navigator.id.genKeyPair(function(pubkey) {
$.ajax({
url: '/api/provision',
data: JSON.stringify({
pubkey: pubkey,
user: email,
"_csrf": $('[name=_csrf]').val()
}),
type: 'POST',
headers: { "Content-Type": 'application/json' },
dataType: 'json',
success: function(r) {
navigator.id.registerCertificate(r.cert);
},
error: function(r) {
navigator.id.raiseProvisioningFailure("could not provision user");
}
});
});
});
})();

Просмотреть файл

@ -1,108 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
$(document).ready(function() {
setTimeout(function() {
$('[name=user]').attr('disabled', 'true');
}, 300);
// XXX: currently the server is plucking email out of get data when
// the auth page is rendered. It does this to render to the user
// their substituted email address.
//
// We need the client to bounce the email off the server and sub it in.
navigator.id.beginAuthentication(function(email) {
var msg;
// all cancel buttons work the same
$("form button.cancel").click(function(e) {
e.preventDefault();
msg = "user canceled authentication";
navigator.id.raiseAuthenticationFailure(msg);
});
$("form").submit(function(e) {
e.preventDefault();
var pass = $.trim($("#pass").val()),
auth_url = $('form').attr('action');
// validate password client side
if (pass.length < 6) {
showTooltip("Yikes, Passwords have to be at least 6 characters",
"#pass");
return;
}
$.ajax({
url: auth_url,
type: 'POST',
data: { user: email, pass: pass, "_csrf": $('[name=_csrf]').val() },
success: function() {
navigator.id.completeAuthentication();
},
error: function() {
showTooltip("The account cannot be logged in with this username and password", "#user");
}
});
});
});
// From here below is tooltip code
var ANIMATION_TIME = 250,
TOOLTIP_MIN_DISPLAY = 2000,
TOOLTIP_OFFSET_TOP_PX = 5,
TOOLTIP_OFFSET_LEFT_PX = 10,
READ_WPM = 200,
hideTimer;
function showTooltip(msg, anchor) {
if (hideTimer) {
clearTimeout(hideTimer);
hideTimer = null;
}
$(".tooltip").hide();
$(".tooltip .contents").text(msg);
$(".tooltip").fadeIn(ANIMATION_TIME);
anchorTooltip(".tooltip", anchor);
var displayTimeMS = calculateDisplayTime(msg);
hideTimeout = setTimeout(function() {
hideTimeout = null;
$(".tooltip").fadeOut(ANIMATION_TIME);
}, displayTimeMS);
}
function anchorTooltip(tooltip, anchor) {
tooltip = $(tooltip);
anchor = $(anchor);
var tooltipOffset = anchor.offset();
tooltipOffset.top -= (tooltip.outerHeight() + TOOLTIP_OFFSET_TOP_PX);
tooltipOffset.left += TOOLTIP_OFFSET_LEFT_PX;
tooltip.css(tooltipOffset);
}
function calculateDisplayTime(text) {
// Calculate the amount of time a tooltip should display based on the
// number of words in the content divided by the number of words an average
// person can read per minute.
var contents = text.replace(/\s+/, ' ').trim(),
words = contents.split(' ').length,
// The average person can read ± 250 wpm.
wordTimeMS = (words / READ_WPM) * 60 * 1000,
displayTimeMS = Math.max(wordTimeMS, TOOLTIP_MIN_DISPLAY);
return displayTimeMS;
}
});

Просмотреть файл

@ -1,245 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// tests of authentication
const
should = require('should'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
fs = require('fs'),
config = require('../server/lib/configuration');
// let's pre-write an alias file for the purposes of this test
describe('authentication', function() {
var context;
it('servers should start', function(done) {
var testUtil = require('./lib/test-util');
testUtil.startServers(function(err, ctx) {
should.not.exist(err);
context = ctx;
done();
});
});
var csrf_token;
it('csrf can be fetched via the api', function(done) {
request.get({
url: util.format('%s/api/session_context', context.mozillaidp.url),
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body).should.be.a('object');
(body.csrf).should.be.a('string');
(resp.headers['cache-control']).should.equal('no-cache, max-age=0');
csrf_token = body.csrf;
done();
});
});
it('auth should fail with incorrect password', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtestwrong',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(401);
(body.success).should.equal(false);
(body.reason).should.equal('email or password incorrect');
done();
});
});
it('auth should fail with malformed email', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(400);
(body.success).should.equal(false);
(body.reason).should.equal('user: ValidatorError: Invalid email');
done();
});
});
it('auth should fail with short password', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: '12345',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(400);
(body.success).should.equal(false);
(body.reason).should.equal('pass: ValidatorError: String is not in range');
done();
});
});
it('auth should fail with extra arguments', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtest',
_csrf: csrf_token,
alias: 'user2+foo@mozilla.com',
email: 'user2+bar@mozilla.com'
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(400);
(body.success).should.equal(false);
(body.reason).should.equal("unsupported parameter: 'alias', 'email'");
done();
});
});
it('auth should fail for DISABLED users', function(done) {
// change the employeetype for a specific user
var user = context.ldap.findUser('user3@mozilla.com');
should.exist(user);
user.attributes.employeetype = 'DISABLED';
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user3@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
(resp.statusCode).should.equal(401);
(body.success).should.equal(false);
// *always* clean up after yourself when editing the mock LDAP directory
// data ... avoids bad things
user.attributes.employeetype = 'Tester'
done();
});
});
it('auth should fail for users not found in ldap', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user-fake@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
(body.success).should.equal(false);
(body.reason).should.equal('email not found');
done();
});
});
it('auth (@mozilla.com) should succeed when correct', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
it('aliased (@mozilla.com) user should authenticate', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'alias2@mozilla.com',
pass: 'testtest',
_csrf: csrf_token,
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
it('auth (@mozillafoundation.org) should succeed when correct', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozillafoundation.org',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
// this user lives under the o=org, dc=mozilla
it('auth (test_a@mozilla.com) should succeed', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'test_a@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
// this user lives under the o=com, dc=mozilla
it('auth (test_a@mozillafoundation.org) should succeed', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'test_a@mozillafoundation.org',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
it('aliased (@mozillafoundation.org) user should authenticate', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'alias2@mozillafoundation.org',
pass: 'testtest',
_csrf: csrf_token,
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
});

Просмотреть файл

@ -1,266 +0,0 @@
// vim: set shiftwidth=2
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// tests of certificate signing
const
should = require('should'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
testUtil = require('./lib/test-util'),
crypto = require('../server/lib/crypto'),
config = require('../server/lib/configuration'),
jwcrypto = require('jwcrypto'),
fs = require('fs');
// load desired algorithms
require("jwcrypto/lib/algs/rs");
require("jwcrypto/lib/algs/ds");
describe('certificate signing', function() {
var context;
it('servers should start', function(done) {
testUtil.startServers(function(err, ctx) {
should.not.exist(err);
context = ctx;
done();
});
});
var keypair;
it('client side keypair generation succeeds', function(done) {
jwcrypto.generateKeypair(
{algorithm: 'DS', keysize: 128},
function(err, kp) {
should.not.exist(err);
keypair = kp;
done();
});
});
var csrf_token;
it('token can be fetched via the api', function(done) {
request.get({
url: util.format('%s/api/session_context', context.mozillaidp.url),
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body).should.be.a('object');
(body.csrf).should.be.a('string');
csrf_token = body.csrf;
done();
});
});
it('signing request fails without proper CSRF', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pubkey: keypair.publicKey.serialize()
}
}, function(err, resp, body) {
should.not.exist(err);
// 403 returned when CSRF is missing
(resp.statusCode).should.equal(403);
done();
});
});
it('signing request fails without an authenticated session', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
// 401 returned when authentication is missing
(resp.statusCode).should.equal(401);
done();
});
});
it('authentication should succeed', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
(resp.headers['cache-control']).should.equal('no-cache, max-age=0');
done();
});
});
it('signing request succeeds with an authenticated session', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
var serverPubKey = jwcrypto.loadPublicKey(crypto.pubKey);
jwcrypto.verify(body.cert, serverPubKey, function(err, payload) {
should.not.exist(err);
(payload.iss).should.equal(config.get('issuer'));
// convert units (s -> ms) and add 10 second padding (certificates are issued
// 10s in the past to mitigate minor clock skew)
(payload.exp - payload.iat).should.equal((config.get('certificate_validity_s') + 10) * 1000);
(payload.iss).should.equal('mozilla.personatest.org');
(payload.principal.email).should.equal('user2@mozilla.com');
done();
});
});
});
it('signing request succeeds for an alias', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'alias2@mozilla.com',
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
var serverPubKey = jwcrypto.loadPublicKey(crypto.pubKey);
jwcrypto.verify(body.cert, serverPubKey, function(err, payload) {
should.not.exist(err);
(payload.iss).should.equal(config.get('issuer'));
// convert units (s -> ms) and add 10 second padding (certificates are issued
// 10s in the past to mitigate minor clock skew)
(payload.exp - payload.iat).should.equal((config.get('certificate_validity_s') + 10) * 1000);
(payload.iss).should.equal('mozilla.personatest.org');
(payload.principal.email).should.equal('alias2@mozilla.com');
done();
});
});
});
it('signing request fails if user is missing', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token
},
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body.success).should.equal(false);
(body.reason).should.equal("missing required parameter: 'user'");
(resp.statusCode).should.equal(400);
done();
});
});
it('signing request fails if pubkey is missing', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
_csrf: csrf_token
},
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body.success).should.equal(false);
(body.reason).should.equal("missing required parameter: 'pubkey'");
(resp.statusCode).should.equal(400);
done();
});
});
it('signing fails when extra params are present', function(done) {
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token,
bogus: 'param'
},
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body.success).should.equal(false);
(body.reason).should.equal("unsupported parameter: 'bogus'");
(resp.statusCode).should.equal(400);
done();
});
});
it('signed fails when employeeType===DISABLED', function(done) {
var user = context.ldap.findUser("user2@mozilla.com");
user.attributes.employeetype = "DISABLED";
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token
}
}, function(err, resp, body) {
user.attributes.employeetype = "Tester";
should.not.exist(err);
(resp.statusCode).should.equal(401);
done();
});
});
it('signing request fails when user has changed their password', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
var user = context.ldap.findUser('user2@mozilla.com');
var oldChangeTime = user.attributes.pwdChangedTime;
user.attributes.pwdChangedTime = "CHANGED"
request.post({
url: util.format('%s/api/provision', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pubkey: keypair.publicKey.serialize(),
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(401);
(resp.body).should.equal('Password Changed. Reauthentication required.');
// make sure the session was reset
should.exist(resp.headers['set-cookie']);
// * reset the value ...
user.attributes.pwdChangedTime = oldChangeTime;
done();
});
});
});
});

Просмотреть файл

@ -1,69 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// tests of CSRF protection
const
should = require('should'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
testUtil = require('./lib/test-util');
describe('CSRF checking', function() {
var context;
it('servers should start', function(done) {
testUtil.startServers(function(err, ctx) {
should.not.exist(err);
context = ctx;
done();
});
});
it('auth should fail without proper token', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtest',
}
}, function(err, resp, body) {
should.not.exist(err);
(body).should.equal('Forbidden');
(resp.statusCode).should.equal(403);
done();
});
});
var csrf_token;
it('token can be fetched via the api', function(done) {
request.get({
url: util.format('%s/api/session_context', context.mozillaidp.url),
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body).should.be.a('object');
(body.csrf).should.be.a('string');
(resp.headers['cache-control']).should.equal('no-cache, max-age=0');
csrf_token = body.csrf;
done();
});
});
it('auth should succeed with proper token', function(done) {
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: 'testtest',
_csrf: csrf_token
}
}, function(err, resp, body) {
should.not.exist(err);
(resp.statusCode).should.equal(200);
done();
});
});
});

Просмотреть файл

@ -1,92 +0,0 @@
const
clorthoServer = require('../server/bin/clortho'),
should = require('should'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
config = require('../server/lib/configuration');
describe('Health Checking', function() {
var serverURL = "";
before(function() {
});
it('the server should startup', function(done) {
clorthoServer.startup(function(err, address){
should.not.exist(err);
serverURL = "http://" + address.address + ":" + address.port;
done();
});
});
it('should return 200 on node_status', function(done) {
request(
util.format('%s%s', serverURL, "/node_status"),
function(err, resp, body) {
resp.statusCode.should.equal(200);
body.should.equal("OK");
done();
}
);
});
it('should return 200 on ldap_status OK', function(done) {
request(
util.format('%s%s', serverURL, "/ldap_status"),
function(err, resp, body) {
resp.statusCode.should.equal(200);
resp.body.should.equal("OK");
done();
}
);
});
it('should return 503 on ldap_status error', function(done) {
// fml, side effects, we need this so when attempting to
// bind it will look for an ldap server that doesn't exist
var ldapurl = config.get("ldap_server_url");
config.set('ldap_server_url', 'ldap://127.0.0.1:1');
request(
util.format('%s%s', serverURL, "/ldap_status"),
function(err, resp, body) {
resp.statusCode.should.equal(503);
config.set('ldap_server_url', ldapurl);
done();
}
);
});
it('should return 503 on ldap_status invalid password', function(done) {
// fml, side effects, we need this so when attempting to
// bind it will look for an ldap server that doesn't exist
var tmp = config.get("ldap_bind_password");
config.set('ldap_bind_password', 'fakkkkkkkkkkeeeee!');
request(
util.format('%s%s', serverURL, "/ldap_status"),
function(err, resp, body) {
resp.statusCode.should.equal(503);
resp.body.should.equal("Error: InvalidCredentialsError");
config.set('ldap_bind_password', tmp);
done();
}
);
});
it('should return 503 on ldap_status invalid DN', function(done) {
// fml, side effects, we need this so when attempting to
// bind it will look for an ldap server that doesn't exist
var tmp = config.get("ldap_bind_dn");
config.set('ldap_bind_dn', 'o=notin,com=thetree');
request(
util.format('%s%s', serverURL, "/ldap_status"),
function(err, resp, body) {
resp.statusCode.should.equal(503);
resp.body.should.equal("Error: NoSuchObjectError");
config.set('ldap_bind_dn', tmp);
done();
}
);
});
});

Просмотреть файл

@ -1,77 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// jshinting (syntax checking) of the source
const
should = require('should'),
fs = require('fs'),
path = require('path'),
jshint = require('jshint').JSHINT,
walk = require('walk');
function jshintFormatter(errors) {
return errors.map(function(e) {
return e.error.reason + ' ' + e.file + ':' + e.error.line;
});
}
describe('source code syntax', function() {
// read jshintrc
var jshintrc;
it('.jshintrc should be readable', function(done) {
jshintrc = JSON.parse(fs.readFileSync(path.join(__dirname, '../.jshintrc')).toString());
(jshintrc).should.be.a('object');
done();
});
var filesToLint = [
path.join(__dirname, '../server/bin/clortho'),
path.join(__dirname, '../server/routes.js')
];
it('we should be able to discover files to lint', function(done) {
var walker = walk.walkSync(path.join(__dirname, '../server/lib'), {});
walker.on("file", function(root, fStat, next) {
var f = path.join(root, fStat.name);
if (/\.js$/.test(f)) {
filesToLint.push(f);
}
next();
});
walker.on("end", done);
});
it('syntax checking should yield no errors', function(done) {
var errors = [];
function checkNext() {
if (!filesToLint.length) {
if (errors.length) {
var buf = util.format("\n %d errors:\n * ",
errors.length);
buf += errors.join("\n * ");
done(buf);
} else {
done(null);
}
return;
}
var f = filesToLint.shift();
fs.readFile(f.toString(), function(err, data) {
// now
f = path.relative(process.cwd(), f);
if (!jshint(data.toString(), jshintrc)) {
jshint.errors.forEach(function(e) {
errors.push(util.format("%s %s:%d - %s", e.id, f, e.line, e.reason));
});
}
checkNext();
});
}
checkNext();
});
});

Просмотреть файл

@ -1,79 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Tests of our ldap abstraction against a local node.js LDAP server
const
should = require('should'),
ldapMock = require('../server/lib/ldapMock')(),
ldap = require('ldapjs'),
auth = require('../server/lib/auth.js'),
testUtil = require('./lib/test-util.js');
var ldapServerInstance = null;
describe('the LDAP authentication library', function() {
before(function(done) {
ldapServerInstance = ldapMock.server;
ldapServerInstance.listen(65077, '127.0.0.1', done);
});
it('should throw when required parameters are missing', function(done) {
(function() {
auth.authUser({
url: 'ldap://127.0.0.1:777',
pass: 'secret'
}, null);
}).should.throw("missing required parameters: dn");
done();
});
it('should fail with an unbound port', function(done) {
auth.authUser({
url: 'ldap://127.0.0.1:777',
dn: 'o=example',
pass: 'secret'
}, function(err) {
should.exist(err);
(err.message).should.equal("connect ECONNREFUSED");
done();
});
});
it('should fail with a bogus ip', function(done) {
auth.authUser({
url: 'ldap://192.192.192.192:777',
dn: 'o=example',
pass: 'secret',
connectTimeout: 30 // ms
}, function(err) {
should.exist(err);
(err).should.equal("connect failed");
done();
});
});
it('should fail with incorrect username', function(done) {
auth.authUser({
url: ldapServerInstance.url,
dn: 'o=badexample',
pass: 'secret'
}, function(err) {
should.exist(err);
(err.message).should.equal("No tree found for: o=badexample");
done();
});
});
it('should succeed when params are correct', function(done) {
auth.authUser({
url: ldapServerInstance.url,
dn: 'mail=user1@mozilla.com, o=com, dc=mozilla',
pass: 'testtest'
}, function(err) {
should.not.exist(err);
done();
});
});
});

Просмотреть файл

@ -1,54 +0,0 @@
// vim: set shiftwidth=2
/* a home for utilities common to all tests. */
// disable logging for tests when VERBOSE isn't defined in the env
if (!process.env['VERBOSE']) {
(require('../../server/lib/logging.js')).disable();
}
const
clorthoServer = require('../../server/bin/clortho'),
config = require('../../server/lib/configuration'),
ldapMock = require('../../server/lib/ldapMock')();
// nothing to export right now!
module.exports = {
// start local LDAP server and mozilla IdP bound to ephemeral ports
startServers: function(cb) {
var ctx = {
mozillaidp: {
url: ''
},
ldap: {
url: '',
instance: null,
directory: ldapMock.directory,
findUser: function(email) {
for(var i=0; i < ldapMock.directory.length; i++) {
if (ldapMock.directory[i].attributes.mail == email) {
return ldapMock.directory[i];
}
}
return null;
}
}
};
// set configuration for ldap server
config.set('ldap_bind_dn', 'mail=user1@mozilla.com, o=com, dc=mozilla');
config.set('ldap_bind_password', 'testtest');
ctx.ldap.instance = ldapMock.server;
ctx.ldap.instance.listen(0, '127.0.0.1', function(err, x) {
if (err) return cb(err);
ctx.ldap.url = ctx.ldap.instance.url;
config.set('ldap_server_url', ctx.ldap.url);
clorthoServer.startup(function(err, address) {
if (err) return cb(err);
ctx.mozillaidp.url = util.format('http://%s:%s',
address.address,
address.port);
cb(null, ctx);
});
});
}
};

Просмотреть файл

@ -1,46 +0,0 @@
const
should = require('should'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
fs = require('fs'),
config = require('../server/lib/configuration');
// let's pre-write an alias file for the purposes of this test
describe('P3P Header', function() {
var context;
it('servers should start', function(done) {
var testUtil = require('./lib/test-util');
testUtil.startServers(function(err, ctx) {
should.not.exist(err);
context = ctx;
done();
});
});
it('P3P headers should be set when IE User-Agent is sent', function(done) {
request.get({
url: util.format('%s/api/session_context', context.mozillaidp.url),
json: true,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US)'
}
}, function(err, resp, body) {
should.not.exist(err);
should.exist(resp.headers['p3p'])
done();
});
});
it('P3P headers should NOT set by default', function(done) {
request.get({
url: util.format('%s/api/session_context', context.mozillaidp.url),
json: true,
}, function(err, resp, body) {
should.not.exist(err);
should.not.exist(resp.headers['p3p'])
done();
});
});
});

Просмотреть файл

@ -1,45 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// tests of crypto routines of the server
const
should = require('should'),
testUtil = require('./lib/test-util'),
request = require('request').defaults({jar: require('request').jar()}),
jwcrypto = require('jwcrypto');
// load desired algorithms
require("jwcrypto/lib/algs/rs");
require("jwcrypto/lib/algs/ds");
var serverURL;
describe('the public key', function() {
it('servers should start', function(done) {
testUtil.startServers(function(err, ctx) {
should.not.exist(err);
serverURL = ctx.mozillaidp.url;
done();
});
});
it('.well-known/browserid should be properly formatted', function(done) {
request(
util.format('%s/.well-known/browserid', serverURL),
function(err, resp, body) {
should.not.exist(err);
should.exist(body);
var parsed = JSON.parse(body);
should.exist(parsed['public-key']);
// now let's parse the public key
var key;
(function() {
key = jwcrypto.loadPublicKey(JSON.stringify(parsed['public-key']));
}).should.not.throw();
should.exist(key);
done();
});
});
});

Просмотреть файл

@ -1,103 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// tests of static file serving
const
should = require('should'),
testUtil = require('./lib/test-util'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
config = require('../server/lib/configuration');
// explicitly disable development mode to test STS headers
config.set('local_development', false);
var securityPolicyValue = util.format("default-src 'self' %s",
config.get('browserid_server'));
var serverURL;
describe('static file serving', function() {
it('the server should startup', function(done) {
testUtil.startServers(function(err, context) {
should.not.exist(err);
serverURL = context.mozillaidp.url;
done();
});
});
var signInPath, provisioningPath;
it('should respond to requests on .well-known/browserid', function(done) {
request(
util.format('%s/.well-known/browserid', serverURL),
function(err, resp, body) {
should.not.exist(err);
should.exist(body);
var parsed = JSON.parse(body);
// content type is important, part of the protocol
(resp.headers['content-type']).should.equal('application/json');
// cache headers are important, controls how long verifiers can cache the public key
(resp.headers['cache-control']).should.equal('max-age=5, public');
// we use STS everywhere
(resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
should.exist(parsed.authentication);
signInPath = parsed.authentication
should.exist(parsed.provisioning);
provisioningPath = parsed.provisioning;
should.exist(parsed['public-key']);
done();
});
});
it('should serve provisioning page', function(done) {
request(
util.format('%s%s', serverURL, provisioningPath),
function(err, resp, body) {
should.not.exist(err);
should.exist(body);
(resp.headers['content-type']).should.equal('text/html; charset=utf-8');
(resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
(resp.headers['x-content-security-policy']).should.equal(
securityPolicyValue);
done();
});
});
it('should serve authenticate page', function(done) {
request(
util.format('%s%s', serverURL, signInPath),
function(err, resp, body) {
should.not.exist(err);
should.exist(body);
(resp.headers['content-type']).should.equal('text/html; charset=utf-8');
(resp.headers['x-frame-options']).should.equal('DENY');
(resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
(resp.headers['x-content-security-policy']).should.equal(
securityPolicyValue);
done();
});
});
it('should serve 404 page', function(done) {
request(
util.format('%s%s', serverURL, "/not_found"),
function(err, resp, body) {
should.not.exist(err);
should.exist(body);
(resp.statusCode).should.equal(404);
(resp.headers['content-type']).should.equal('text/html; charset=utf-8');
(resp.headers['x-frame-options']).should.equal('DENY');
(resp.headers['strict-transport-security']).should.equal("max-age=10886400; includeSubdomains");
(resp.headers['x-content-security-policy']).should.equal(
securityPolicyValue);
done();
});
});
});

Просмотреть файл

@ -1,93 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// tests of authentication throttling
const
should = require('should'),
request = require('request').defaults({jar: require('request').jar()}),
util = require('util'),
testUtil = require('./lib/test-util'),
config = require('../server/lib/configuration');
describe('authentication throttling', function() {
var context;
function authNTimes(n, password, cb, _lastErr) {
if (n === 0) return cb(_lastErr);
request.post({
url: util.format('%s/api/sign_in', context.mozillaidp.url),
json: {
user: 'user2@mozilla.com',
pass: password,
_csrf: csrf_token
}
}, function(err, resp, body) {
var curErr = resp.statusCode === 200 ? null : 'failed';
authNTimes(--n, password, cb, err || curErr);
});
}
it('servers startup as expected', function(done) {
testUtil.startServers(function(err, ctx) {
should.not.exist(err);
context = ctx;
done();
});
});
var csrf_token;
it('a CSRF token can be fetched', function(done) {
request.get({
url: util.format('%s/api/session_context', context.mozillaidp.url),
json: true
}, function(err, resp, body) {
should.not.exist(err);
(body).should.be.a('object');
(body.csrf).should.be.a('string');
csrf_token = body.csrf;
done();
});
});
it('auth will succeed 5 consecutive times', function(done) {
authNTimes(5, "testtest", done);
});
it('auth may fail 4 consecutive times', function(done) {
authNTimes(4, "badpassword", function(err) {
(err).should.equal('failed');
done();
});
});
it('auth should succeed on the fifth', function(done) {
authNTimes(1, "testtest", done);
});
it('auth may fail 5 consecutive times', function(done) {
authNTimes(5, "badpassword", function(err) {
(err).should.equal('failed');
done();
});
});
it('which results in a locked out user', function(done) {
authNTimes(1, "testtest", function(err) {
done(err === 'failed' ? null : "failed to throttle");
});
});
it('user is locked out with a bad password', function(done) {
authNTimes(1, "bad password", function(err) {
done(err === 'failed' ? null : "failed to throttle");
});
});
it('user is able to login after auth_lockout_ms expires', function(done) {
config.set('auth_lockout_ms', 10);
setTimeout(function() {
authNTimes(1, "testtest", done);
}, 20);
});
});