├── .gitignore ├── .openshift ├── action_hooks │ ├── build │ ├── deploy │ ├── post_deploy │ ├── post_start_nodejs │ ├── post_start_nodejs-0.6 │ ├── post_stop_nodejs │ ├── post_stop_nodejs-0.6 │ ├── pre_build │ ├── pre_start_nodejs │ ├── pre_start_nodejs-0.6 │ ├── pre_stop_nodejs │ └── pre_stop_nodejs-0.6 ├── cron │ ├── README.cron │ ├── daily │ │ └── .gitignore │ ├── hourly │ │ └── .gitignore │ ├── minutely │ │ └── .gitignore │ ├── monthly │ │ └── .gitignore │ └── weekly │ │ ├── README │ │ ├── chrono.dat │ │ ├── chronograph │ │ ├── jobs.allow │ │ └── jobs.deny ├── lib │ ├── setup_custom_nodejs_env │ └── utils └── markers │ ├── NODEJS_VERSION │ └── README ├── LICENSE ├── README ├── README.md ├── deplist.txt ├── jmeter ├── JMeter.md ├── snowflake.jmx └── snowflakeUsers.csv ├── package.json ├── server.js └── src ├── assets ├── github-markdown.css └── resetPassword.js ├── auth └── jwt-strategy.js ├── config.sample.js ├── config ├── hapi.js ├── methods.js ├── plugins.js ├── routes.js └── views.js ├── database ├── models │ └── User.js ├── mongodb.js └── redis.js ├── docs ├── README.md └── home.md ├── lib ├── Crypto.js └── Mailer.js ├── routes ├── account │ ├── endpoints.js │ └── handlers.js ├── general │ ├── endpoints.js │ └── handlers.js └── restricted │ ├── endpoints.js │ └── handlers.js └── views └── resetpassword.html /.gitignore: -------------------------------------------------------------------------------- 1 | .tern-project 2 | node_modules 3 | .tern-port 4 | .DS_Store 5 | config.js -------------------------------------------------------------------------------- /.openshift/action_hooks/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This is a simple build script and will be executed on your CI system if 3 | # available. Otherwise it will execute while your application is stopped 4 | # before the deploy step. This script gets executed directly, so it 5 | # could be python, php, ruby, etc. 6 | 7 | 8 | # Source utility functions. 9 | source "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" 10 | 11 | # Setup path to include the custom Node[.js] version. 12 | _SHOW_SETUP_PATH_MESSAGES="true" setup_path_for_custom_node_version 13 | 14 | 15 | # So we we moved the package.json file out of the way in pre_build, 16 | # so that the OpenShift git post-receive hook doesn't try and use the 17 | # old npm version to install the dependencies. Move it back in. 18 | tmp_package_json="$(get_node_tmp_dir)/package.json" 19 | if [ -f "$tmp_package_json" ]; then 20 | # Only overlay it if there is no current package.json file. 21 | [ -f "${OPENSHIFT_REPO_DIR}/package.json" ] || \ 22 | mv "$tmp_package_json" "${OPENSHIFT_REPO_DIR}/package.json" 23 | fi 24 | 25 | 26 | # Do npm install with the new npm binary. 27 | if [ -f "${OPENSHIFT_REPO_DIR}"/package.json ]; then 28 | echo " - Installing dependencies w/ new version of npm ... " 29 | echo 30 | (cd "${OPENSHIFT_REPO_DIR}"; export TMPDIR="/tmp"; npm install -d) 31 | fi 32 | 33 | -------------------------------------------------------------------------------- /.openshift/action_hooks/deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This deploy hook gets executed after dependencies are resolved and the 3 | # build hook has been run but before the application has been started back 4 | # up again. This script gets executed directly, so it could be python, php, 5 | # ruby, etc. 6 | 7 | 8 | # Source utility functions. 9 | source "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" 10 | 11 | 12 | # On slave/serving gears, need to do the install as part of deploy 13 | # so check if its needed. Just ensure the custom Node[.js] version is 14 | # installed. 15 | ensure_node_is_installed 16 | 17 | -------------------------------------------------------------------------------- /.openshift/action_hooks/post_deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This is a simple post deploy hook executed after your application 3 | # is deployed and started. This script gets executed directly, so 4 | # it could be python, php, ruby, etc. 5 | -------------------------------------------------------------------------------- /.openshift/action_hooks/post_start_nodejs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED* 4 | # immediately before (re)starting or stopping the specified cartridge. 5 | # They are able to make any desired environment variable changes as 6 | # well as other adjustments to the application environment. 7 | 8 | # The post_start_cartridge and post_stop_cartridge hooks are executed 9 | # immediately after (re)starting or stopping the specified cartridge. 10 | 11 | # Exercise caution when adding commands to these hooks. They can 12 | # prevent your application from stopping cleanly or starting at all. 13 | # Application start and stop is subject to different timeouts 14 | # throughout the system. 15 | 16 | # Source utility functions. 17 | source "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" 18 | 19 | # Get the node version. 20 | ver=$(get_node_version) 21 | 22 | 23 | # Set URI to the custom sample /env route. 24 | app_uri="http://$OPENSHIFT_GEAR_DNS/env" 25 | 26 | # Wait a bit to give the server time to come up. 27 | sleep 5 28 | 29 | # Check if the app_uri is available - user code could have removed the 30 | # one in the sample -- we just print this for sanity testing. 31 | zcode=$(curl --write-out %{http_code} -s -o /dev/null "$app_uri") 32 | 33 | if [ "$zcode" -eq 200 ]; then 34 | echo "" 35 | echo " - Using Node.js version $ver, checking app URI ... " 36 | echo " - test URI = $app_uri" 37 | echo " - Version from test URI = $(curl -s $app_uri | grep 'Version')" 38 | echo "" 39 | fi 40 | 41 | 42 | -------------------------------------------------------------------------------- /.openshift/action_hooks/post_start_nodejs-0.6: -------------------------------------------------------------------------------- 1 | post_start_nodejs -------------------------------------------------------------------------------- /.openshift/action_hooks/post_stop_nodejs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED* 4 | # immediately before (re)starting or stopping the specified cartridge. 5 | # They are able to make any desired environment variable changes as 6 | # well as other adjustments to the application environment. 7 | 8 | # The post_start_cartridge and post_stop_cartridge hooks are executed 9 | # immediately after (re)starting or stopping the specified cartridge. 10 | 11 | # Exercise caution when adding commands to these hooks. They can 12 | # prevent your application from stopping cleanly or starting at all. 13 | # Application start and stop is subject to different timeouts 14 | # throughout the system. 15 | -------------------------------------------------------------------------------- /.openshift/action_hooks/post_stop_nodejs-0.6: -------------------------------------------------------------------------------- 1 | post_stop_nodejs -------------------------------------------------------------------------------- /.openshift/action_hooks/pre_build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This is a simple script and will be executed on your CI system if 3 | # available. Otherwise it will execute while your application is stopped 4 | # before the build step. This script gets executed directly, so it 5 | # could be python, php, ruby, etc. 6 | 7 | 8 | # Source utility functions. 9 | source "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" 10 | 11 | # Ensure custom node version if not installed. 12 | echo "" 13 | ensure_node_is_installed 14 | 15 | 16 | # We need to move the package.json file out of the way in pre_build, so 17 | # that the OpenShift git post-receive hook doesn't try and use the old 18 | # npm version to install the dependencies. 19 | mv "${OPENSHIFT_REPO_DIR}/package.json" "$(get_node_tmp_dir)" 20 | 21 | 22 | -------------------------------------------------------------------------------- /.openshift/action_hooks/pre_start_nodejs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED* 4 | # immediately before (re)starting or stopping the specified cartridge. 5 | # They are able to make any desired environment variable changes as 6 | # well as other adjustments to the application environment. 7 | 8 | # The post_start_cartridge and post_stop_cartridge hooks are executed 9 | # immediately after (re)starting or stopping the specified cartridge. 10 | 11 | # Exercise caution when adding commands to these hooks. They can 12 | # prevent your application from stopping cleanly or starting at all. 13 | # Application start and stop is subject to different timeouts 14 | # throughout the system. 15 | 16 | 17 | # Source utility functions. 18 | source "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" 19 | 20 | # Setup path to include the custom Node[.js] version. 21 | ver=$(get_node_version) 22 | echo "" 23 | echo " - pre_start_nodejs: Adding Node.js version $ver binaries to path" 24 | _SHOW_SETUP_PATH_MESSAGES="true" setup_path_for_custom_node_version 25 | 26 | -------------------------------------------------------------------------------- /.openshift/action_hooks/pre_start_nodejs-0.6: -------------------------------------------------------------------------------- 1 | pre_start_nodejs -------------------------------------------------------------------------------- /.openshift/action_hooks/pre_stop_nodejs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED* 4 | # immediately before (re)starting or stopping the specified cartridge. 5 | # They are able to make any desired environment variable changes as 6 | # well as other adjustments to the application environment. 7 | 8 | # The post_start_cartridge and post_stop_cartridge hooks are executed 9 | # immediately after (re)starting or stopping the specified cartridge. 10 | 11 | # Exercise caution when adding commands to these hooks. They can 12 | # prevent your application from stopping cleanly or starting at all. 13 | # Application start and stop is subject to different timeouts 14 | # throughout the system. 15 | 16 | 17 | # First time in .openshift/lib/utils might not exist, so add a check 18 | # to ensure file exists. 19 | if [ -f "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" ]; then 20 | # Source utility functions. 21 | source "$OPENSHIFT_REPO_DIR/.openshift/lib/utils" 22 | 23 | # Setup path to include the custom Node[.js] version. 24 | ver=$(get_node_version) 25 | echo "" 26 | echo " - pre_stop_nodejs: Adding Node.js version $ver binaries to path" 27 | _SHOW_SETUP_PATH_MESSAGES="true" setup_path_for_custom_node_version 28 | fi 29 | 30 | -------------------------------------------------------------------------------- /.openshift/action_hooks/pre_stop_nodejs-0.6: -------------------------------------------------------------------------------- 1 | pre_stop_nodejs -------------------------------------------------------------------------------- /.openshift/cron/README.cron: -------------------------------------------------------------------------------- 1 | Run scripts or jobs on a periodic basis 2 | ======================================= 3 | Any scripts or jobs added to the minutely, hourly, daily, weekly or monthly 4 | directories will be run on a scheduled basis (frequency is as indicated by the 5 | name of the directory) using run-parts. 6 | 7 | run-parts ignores any files that are hidden or dotfiles (.*) or backup 8 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} 9 | 10 | The presence of two specially named files jobs.deny and jobs.allow controls 11 | how run-parts executes your scripts/jobs. 12 | jobs.deny ===> Prevents specific scripts or jobs from being executed. 13 | jobs.allow ===> Only execute the named scripts or jobs (all other/non-named 14 | scripts that exist in this directory are ignored). 15 | 16 | The principles of jobs.deny and jobs.allow are the same as those of cron.deny 17 | and cron.allow and are described in detail at: 18 | http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Automating_System_Tasks.html#s2-autotasks-cron-access 19 | 20 | See: man crontab or above link for more details and see the the weekly/ 21 | directory for an example. 22 | 23 | PLEASE NOTE: The Cron cartridge must be installed in order to run the configured jobs. 24 | -------------------------------------------------------------------------------- /.openshift/cron/daily/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartonhammond/snowflake-hapi-openshift/be9cc7f2052393bbd1b2efeccaacd80bae887bc0/.openshift/cron/daily/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/hourly/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartonhammond/snowflake-hapi-openshift/be9cc7f2052393bbd1b2efeccaacd80bae887bc0/.openshift/cron/hourly/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/minutely/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartonhammond/snowflake-hapi-openshift/be9cc7f2052393bbd1b2efeccaacd80bae887bc0/.openshift/cron/minutely/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/monthly/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartonhammond/snowflake-hapi-openshift/be9cc7f2052393bbd1b2efeccaacd80bae887bc0/.openshift/cron/monthly/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/weekly/README: -------------------------------------------------------------------------------- 1 | Run scripts or jobs on a weekly basis 2 | ===================================== 3 | Any scripts or jobs added to this directory will be run on a scheduled basis 4 | (weekly) using run-parts. 5 | 6 | run-parts ignores any files that are hidden or dotfiles (.*) or backup 7 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} and handles 8 | the files named jobs.deny and jobs.allow specially. 9 | 10 | In this specific example, the chronograph script is the only script or job file 11 | executed on a weekly basis (due to white-listing it in jobs.allow). And the 12 | README and chrono.dat file are ignored either as a result of being black-listed 13 | in jobs.deny or because they are NOT white-listed in the jobs.allow file. 14 | 15 | For more details, please see ../README.cron file. 16 | 17 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/chrono.dat: -------------------------------------------------------------------------------- 1 | Time And Relative D...n In Execution (Open)Shift! 2 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/chronograph: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "`date`: `cat $(dirname \"$0\")/chrono.dat`" 4 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/jobs.allow: -------------------------------------------------------------------------------- 1 | # 2 | # Script or job files listed in here (one entry per line) will be 3 | # executed on a weekly-basis. 4 | # 5 | # Example: The chronograph script will be executed weekly but the README 6 | # and chrono.dat files in this directory will be ignored. 7 | # 8 | # The README file is actually ignored due to the entry in the 9 | # jobs.deny which is checked before jobs.allow (this file). 10 | # 11 | chronograph 12 | 13 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/jobs.deny: -------------------------------------------------------------------------------- 1 | # 2 | # Any script or job files listed in here (one entry per line) will NOT be 3 | # executed (read as ignored by run-parts). 4 | # 5 | 6 | README 7 | 8 | -------------------------------------------------------------------------------- /.openshift/lib/setup_custom_nodejs_env: -------------------------------------------------------------------------------- 1 | # Utility functions for bash session - sourced in via the user's 2 | # bash profile ($OPENSHIFT_DATA_DIR/.bash_profile). 3 | 4 | # Source utility functions. 5 | source $OPENSHIFT_REPO_DIR/.openshift/lib/utils 6 | 7 | 8 | # Internal function to setup path and remove the wrappers. 9 | function _setup_path_and_remove_wrappers() { 10 | # First invocation of npm or node, so setup the custom path and 11 | # unset the wrappers. Add the custom node binaries to the PATH. 12 | [ -z "$ZDEBUG" ] || echo "Setting path to include custom Node version" 13 | setup_path_for_custom_node_version 14 | unset node 15 | unset npm 16 | unset _setup_path_and_remove_wrappers 17 | 18 | } # End of function _setup_path_and_remove_wrappers. 19 | 20 | 21 | # Temporary wrapper function to setup path before invoking npm. 22 | function npm() { 23 | # Setup path, remove wrappers and reinvoke npm. 24 | _setup_path_and_remove_wrappers 25 | npm "$@" 26 | 27 | } # End of function npm. 28 | 29 | 30 | # Temporary wrapper function to setup path before invoking node. 31 | function node() { 32 | # Setup path, remove wrappers and reinvoke node. 33 | _setup_path_and_remove_wrappers 34 | node "$@" 35 | 36 | } # End of function node. 37 | 38 | 39 | # 40 | # EOF 41 | -------------------------------------------------------------------------------- /.openshift/lib/utils: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Utility functions. 4 | # 5 | 6 | 7 | # Returns the configured Node version - defaults to 0.8.24. 8 | function get_node_version() { 9 | marker="$OPENSHIFT_REPO_DIR/.openshift/markers/NODEJS_VERSION" 10 | nodejs_ver=$(egrep -v "^\s*#.*" "$marker" | egrep -v "^\s*$" | tail -1) 11 | echo "${nodejs_ver:-"0.8.24"}" 12 | 13 | } # End of function get_node_version. 14 | 15 | 16 | # Returns the directory where Node is to be installed/is installed. 17 | function get_node_install_dir() { 18 | echo "$OPENSHIFT_DATA_DIR" 19 | 20 | } # End of function get_node_install_dir. 21 | 22 | 23 | # Returns the path to the npm binary. 24 | function get_npm_bin_path() { 25 | ver=${1:-"$(get_node_version)"} 26 | echo "$(get_node_install_dir)/node-v$ver-linux-x64/bin" 27 | 28 | } # End of function get_npm_bin_path. 29 | 30 | 31 | # Returns the path to the node binary. 32 | function get_node_bin_path() { 33 | echo "$(get_npm_bin_path $@)" 34 | 35 | } # End of function get_node_bin_path. 36 | 37 | 38 | # Returns the temporary directory we use for processing. 39 | function get_node_tmp_dir() { 40 | ztmpdir="$OPENSHIFT_DATA_DIR/.nodejs.tmp" 41 | 42 | # Ensure temp directory is created. 43 | [ -d "$ztmpdir" ] || mkdir -p "$ztmpdir" 44 | 45 | echo "$ztmpdir" 46 | 47 | } # End of function get_node_tmp_dir. 48 | 49 | 50 | # 51 | # Download and install the specified Node.js version. 52 | # 53 | function _install_nodejs() { 54 | ver=${1:-"$(get_node_version)"} 55 | 56 | # Sample download links: 57 | # http://nodejs.org/dist/v0.8.24/node-v0.8.24-linux-x64.tar.gz 58 | # http://nodejs.org/dist/v0.10.10/node-v0.10.10-linux-x64.tar.gz 59 | zfile="node-v${ver}-linux-x64.tar.gz" 60 | zlink="http://nodejs.org/dist/v${ver}/${zfile}" 61 | 62 | instdir="$(get_node_install_dir)" 63 | 64 | # Download and extract the gzipped tarball. 65 | dldir="$OPENSHIFT_DATA_DIR/downloads" 66 | mkdir -p "$dldir" 67 | 68 | echo " - Downloading and extracting $zlink ... " 69 | 70 | if ! curl -L -o "$dldir/$zfile" "$zlink"; then 71 | echo " - ERROR -- download failed for $zlink" 72 | echo " - download uri = $dldir/$zfile" 73 | return 1 74 | fi 75 | 76 | (cd "$instdir"; tar -zxf "$dldir/$zfile") 77 | echo " - Done installing Node.js version $ver" 78 | 79 | } # End of function _install_nodejs. 80 | 81 | 82 | # Ensure npm and node symlinks exist in ~/.node_modules/.bin/ directory. 83 | function _ensure_symlinks_in_node_modules_bin_dir() { 84 | zdir="$HOME/.node_modules/.bin/" 85 | if [ -d "$zdir" ]; then 86 | ln -sf "$(get_npm_bin_path)/npm" "$zdir" 87 | ln -sf "$(get_node_bin_path)/node" "$zdir" 88 | fi 89 | 90 | } # End of function _ensure_symlinks_in_node_modules_bin_dir. 91 | 92 | 93 | # Ensure context - this is a bit too hacky. 94 | function _ensure_context_exists() { 95 | zenv="$OPENSHIFT_HOMEDIR/nodejs/configuration/node.env" 96 | if ! egrep '^\s*function\s+nodejs_context' "$zenv" > /dev/null 2>&1; then 97 | echo -e "function nodejs_context() { bash -c \"\$@\"; }\n" >> "$zenv" || : 98 | fi 99 | 100 | } # End of function _ensure_context_exists. 101 | 102 | 103 | # Ensure the shell env setup bits are added to user's .bash_profile. 104 | function _ensure_bash_profile_setup() { 105 | dot_bash_profile=$OPENSHIFT_DATA_DIR/.bash_profile 106 | pattern='\s*source(.*)\.openshift/lib/setup_custom_nodejs_env\s*(.*)\s*' 107 | if ! egrep "$pattern" $dot_bash_profile > /dev/null 2>&1 ; then 108 | 109 | cat >> $dot_bash_profile < | hapi | mongodb | 10 | | jmeter | swagger | redis| 11 | | blazeMeter| nodeMailer | mongoose | 12 | 13 | 14 | * **OpenShift** [https://www.openshift.com/](https://www.openshift.com/) - OpenShift Online is Red Hat's next-generation application hosting platform that makes it easy to run your web applications in the cloud for free. 15 | * **Hapi** [http://hapijs.com/](http://hapijs.com/) - A rich framework for building applications and services 16 | * **MongoDb** [https://www.mongodb.org](https://www.mongodb.org) - MongoDBis an open-source, document database designed for ease of development and scaling. 17 | * **Mongoose** [http://mongoosejs.com/](http://mongoosejs.com/) - elegant mongodb object modeling for node.js 18 | * **Redis** [http://redis.io/](http://redis.io/) - Redis is an open source, in-memory data structure store, used as database, cache and message broker. 19 | * **Swagger** [http://swagger.io/](http://swagger.io/) - The World's Most Popular Framework for APIs. 20 | * **Jason Web Token** [https://github.com/auth0/node-jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. 21 | * **NodeMailer** [http://nodemailer.com/](http://nodemailer.com/) - Send e-mails with Node.JS – easy as cake! 22 | * **BlazeMeter** [https://blazemeter.com/](https://blazemeter.com/) -Run massively scalable, open source-based performance tests against all of your apps, from classic 23 | web and mobile to microservices and APIs, and validate performance at every software delivery stage. 24 | * **JMeter** [http://jmeter.apache.org/]([http://jmeter.apache.org/) - The Apache JMeter™ application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance 25 | 26 | 27 | [![Join the chat at https://gitter.im/bartonhammond/snowflake](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bartonhammond/snowflake?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/bartonhammond/snowflake/blob/master/LICENSE) 28 | 29 | # Content 30 | - [Use case](#usecase) 31 | - [Summary](#summary) 32 | - [Setup](#setup) 33 | - [API Documentation](http://mysnowflake-bartonhammond.rhcloud.com/documentation) 34 | - [Source documentation](http://bartonhammond.github.io/snowflake-hapi-openshift/server.js.html) 35 | 36 | ## Usecase 37 | 38 | * **Register** - When user registers w/ email, username, password, the 39 | system sends a email verification. Until the user clicks the link 40 | within the email, their Email Verified field is false. When 41 | clicked, the field is set to true. The response from Register 42 | contains a "Session Token" for subsequent authentication. 43 | 44 | * **Login** When the user logs in with their username and password, 45 | the sytem responds with a "Session Token". 46 | 47 | * **Log Out** When the user logs out, the Session Token is blacklisted 48 | using Redis. Every entry point to the server that requires 49 | authentication first checks if the Session Toke has already been 50 | revoked by checking it's presence in Redis. If present, the request 51 | is denied. 52 | 53 | * **Reset Password** Once the user provides an Email address, they 54 | receive an email with a link that takes them to a form to submit a 55 | new password 56 | 57 | * **Profile** Once a user is logged in, they can view their profile. 58 | 59 | * **Profile Update** A User can modify their username and/or email. 60 | If the email address is modified, their Email Verified value is 61 | set again to false and a new 62 | email is sent to the new address for verification. 63 | 64 | 65 | ## Summary 66 | 67 | ### OpenShift 68 | OpenShift supports a free NodeJS setup that will scale with web 69 | traffic. This **Snowflake Server** setup will use MongoDB and Redis. 70 | ![Open Shift Dashboard](https://cloud.githubusercontent.com/assets/1282364/12080387/4f44e074-b21e-11e5-97d0-244573cc7460.png) 71 | * This server is using 3 small gears: 72 | * NodeJS (actually at version 4.2.3) 73 | * MongoDB 74 | * Redis 75 | * And a **Web Load Balancer** 76 | 77 | Some commands that you'll want to know about, once you've install the 78 | 'rhc' client: 79 | 80 | * rhc ssh -a mysnowflake 81 | 82 | You can check the performance of your application using the link 83 | 84 | ``` 85 | your-app-domain/haproxy-status/ 86 | ``` 87 | 88 | The HAProxy Status Page 89 | ![HAProxy](https://cloud.githubusercontent.com/assets/1282364/12080273/4f1806c4-b21b-11e5-8293-065ee2ab8ea7.png) 90 | 91 | 92 | ### Hapi 93 | The nodeJS server uses Hapi. Hapi was developed and Open Sourced by 94 | [Walmart Labs](http://www.walmartlabs.com/project_type/open-source/). 95 | It has been battle tested by Walmart, the largest retailer on earth. 96 | I chose it over Express 'cause Hapi is more targeted to API support 97 | and it looked interesting. 98 | 99 | This server is documented [here](http://bartonhammond.github.io/snowflake-hapi-openshift/server.js.html) in its entirety. 100 | 101 | Here's some flavor of what Hapi offers. Below is the declarative definition of the ```/account/login``` end point. The ```payload``` is validated here and shows how the ```username``` has a regex expression and is required. The same for the ```email```. The ```config``` option has the ```tags, description, notes``` that document how the ```api``` is used. The ```handler``` is defined elsewhere. Separating the end point validation and declaration from the implementation cleans up the code. 102 | 103 | ``` 104 | { 105 | method: 'POST', 106 | path: '/account/login', 107 | handler: AccountHandlers.loginUser, 108 | config: { 109 | // Include this API in swagger documentation 110 | tags: ['api'], 111 | description: 'A user can login', 112 | notes: 'The user login will return a sessionToken', 113 | validate: { 114 | payload: { 115 | //username required with same regex as client 116 | username: Joi.string().regex(CONFIG.validation.username).required(), 117 | //password required with same regex as client 118 | password: Joi.string().regex(CONFIG.validation.password).required() 119 | } 120 | } 121 | } 122 | }, 123 | ``` 124 | 125 | 126 | ### MongoDb 127 | Mongodb will host our documents, namely User information, at this 128 | time. We'll be using **Mongoose** for interacting with Mongo within 129 | our code. 130 | 131 | Once you're ssh'd into Openshift via ```rhc ssh -a mysnowflake```, you 132 | can use the ```mongo``` shell. 133 | 134 | 135 | ### Redis 136 | Redis is fantastic for key,value pair access. We're using it here for 137 | "Black Listing Json Web Tokens". You can read about this concept here [https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/](https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/) 138 | 139 | 140 | ### Swagger 141 | Swagger provides the api documentation - simply augmenting the 142 | endpoints generates a page showing all the API access points. 143 | 144 | Shown below is the generated API documentation - 145 | 146 | ![Swagger docs](https://cloud.githubusercontent.com/assets/1282364/12080462/67b642e0-b220-11e5-905e-0238432e496b.png) 147 | 148 | NOTE: you can test the APIs directly from the browser with the forms that Swagger provides! 149 | 150 | ![Swagger form](https://cloud.githubusercontent.com/assets/1282364/12080686/6d358390-b228-11e5-917e-23443bff39d6.png) 151 | 152 | ### Jason Web Token 153 | 154 | JWT is used in the Authentication as a Session Token. You can read the docs [here](http://bartonhammond.github.io/snowflake-hapi-openshift/src/auth/jwt-strategy.js.html) showing how it's setup. 155 | 156 | 157 | ### JMeter 158 | 159 | Using JMeter allowed me to performance test the API. I created a test suite with JMeter as shown below and debugged the script by running locally. Once I was satisfied, I changed the ```HTTP Request Defaults``` and uploaded to BlazeMeter for testing. 160 | 161 | Shown below is the script defined in JMeter 162 | 163 | ![JMeter setup](https://cloud.githubusercontent.com/assets/1282364/12080705/1b0c4ad0-b229-11e5-8690-08430d50b865.png) 164 | 165 | ### BlazeMeter 166 | BlazeMeter was used to perform the tests as it is much better equipped to host the threads then my personal mac. 167 | 168 | 169 | Running BlazeMeter 171 | 172 | 173 | The following screens show the results of running 50 concurrent users performing the following actions with a 1 second delay between each action: 174 | 175 | * **register** - Register with username, password, and email 176 | * **login** - Usename, password sued to login 177 | * **restricted access** - access a page requiring SessionToken 178 | * **profile/account/me** - show my profile 179 | * **profile/account/{_id}** - update the profile defined by _id 180 | 181 | **Original Test Configuration** 182 | ![Original Test Configuration](https://cloud.githubusercontent.com/assets/1282364/12080259/f004f1d8-b21a-11e5-8675-e81f4f915842.png) 183 | 184 | **Overview** 185 | ![Overview](https://cloud.githubusercontent.com/assets/1282364/12080257/effc7008-b21a-11e5-981f-97c59494ff16.png) 186 | 187 | **Timeline Report** 188 | ![Timeline Report](https://cloud.githubusercontent.com/assets/1282364/12080258/effeaddc-b21a-11e5-95c8-72e6d9960092.png) 189 | 190 | **Load Report** 191 | ![Load Report](https://cloud.githubusercontent.com/assets/1282364/12080260/f004d7e8-b21a-11e5-8c42-1763e2c83945.png) 192 | 193 | **Aggregate Report** 194 | ![Aggregate Report](https://cloud.githubusercontent.com/assets/1282364/12080261/f00705cc-b21a-11e5-8a33-c19a68c4abe1.png) 195 | 196 | **Monitoring Report** 197 | ![Monitoring Report](https://cloud.githubusercontent.com/assets/1282364/12080262/f0071224-b21a-11e5-82f7-c1f2dd5bfd58.png) 198 | 199 | ------------------- 200 | 201 | 202 | ## Setup 203 | 204 | Below are the instructions for setting up the server on your local machine. These instructions work fine on the Mac - no promise is made for other OSs. 205 | 206 | * **Pre-setup step**: We are using **NodeMailer** and if you use a Gmail account, be sure to follow the steps below from [https://github.com/nodemailer/nodemailer](https://github.com/nodemailer/nodemailer) 207 | 208 | 209 | > You may need to "Allow Less Secure Apps" in your gmail account (it's all the way at the bottom). You also may need to "Allow access to your Google account" 210 | 211 | 212 | 213 | ### Locally (one time only setup) 214 | ---------------------------------------------------------- 215 | * Install Mongo db 216 | [https://www.mongodb.org/downloads#production](https://www.mongodb.org/downloads#production) 217 | * to start, ```sudo mongod``` 218 | 219 | * Install Redis 220 | 221 | * I used Redis 2.18.24, which is what's installed on OpenShift 222 | * down load and unzip [http://download.redis.io/releases/redis-2.8.24.tar.gz](http://download.redis.io/releases/redis-2.8.24.tar.gz) 223 | 224 | ``` 225 | cd redis-2.8.24 226 | make 227 | cd src/ 228 | ./redis-server 229 | ``` 230 | 231 | * Update ip in config file w/ ip from `ifconfig` 232 | Example: 233 | ``` 234 | hapi: { 235 | port: 5000, 236 | ip: '127.0.0.1' 237 | } 238 | 239 | ``` 240 | 241 | * Update Snowflake ```src/lib/config.js``` w/ same ip from step above 242 | Example: 243 | ``` 244 | HAPI: { 245 | local: { 246 | url: 'http://127.0.0.1:5000' 247 | }, 248 | remote: { 249 | url: 'enter your remote url here' 250 | } 251 | } 252 | ``` 253 | 254 | * Then in different terminal, inside mysnowflake, 255 | 256 | ``` 257 | npm start 258 | ``` 259 | 260 | 261 | ### OpenShift 262 | 263 | Watch the following video to see all the steps to install the **Hapi nodes server to Openshift** in action: 264 | 265 | Snowflake Hot Loading 267 | 268 | 269 | ---------------------------------------------------------- 270 | 271 | * Create an account at http://openshift.redhat.com/ 272 | 273 | * Install the command line tool ```rhc``` 274 | 275 | * [https://developers.openshift.com/en/managing-client-tools.html](https://developers.openshift.com/en/managing-client-tools.html) 276 | 277 | * Create a namespace, if you haven't already done so via the web interface 278 | 279 | ``` 280 | rhc domain create 281 | ``` 282 | 283 | * Create a nodejs application with mongodb. The ```-s``` option is for "Scaling" 284 | 285 | ``` 286 | rhc app-create mysnowflake nodejs-0.10 mongodb-2.4 -s 287 | 288 | ``` 289 | 290 | * Note that if you get an error during this step, most likely it has to do with copying the Openshift GIT repository to your local system. What you can do is to your OpenShift account and use the link they provided for the **Source Code**. Just ```git clone xxx``` where xxx is the link you copied from Openshift. 291 | 292 | 293 | * This next command will load the Redis cartridge 294 | 295 | ``` 296 | rhc add-cartridge \ 297 | http://cartreflect-claytondev.rhcloud.com/reflect?github=transformatordesign/openshift-redis-cart \ 298 | -a mysnowflake 299 | ``` 300 | 301 | 302 | * These next few steps copy the SnowFlake server to your local Git repository from Openshift. 303 | 304 | ``` 305 | cd mysnowflake 306 | git remote add upstream -m master git://github.com/bartonhammond/snowflake-hapi-openshift.git 307 | git pull -s recursive -X theirs upstream master 308 | ``` 309 | 310 | * Copy config.sample.js to config.js and provide values 311 | 312 | ``` 313 | cp src/config.sample.js src/config.js 314 | ``` 315 | 316 | * Then push the repo to OpenShift 317 | 318 | ``` 319 | git push origin master 320 | ``` 321 | 322 | * **That's it!!!** 323 | 324 | * You can now checkout your application at:[ http://mysnowflake-$yournamespace.rhcloud.com](http://mysnowflake-$yournamespace.rhcloud.com/) 325 | 326 | 327 | * If you want to have this project also in your personal GitHub account also, follow these steps but be **WARNED**: unless you use a "Private" repository, your 328 | ```config.js``` will be visible. 329 | 330 | * https://forums.openshift.com/how-to-keep-a-github-repository-and-an-openshift-repository-in-sync 331 | -------------------------------------------------------------------------------- /deplist.txt: -------------------------------------------------------------------------------- 1 | # 2 | # *************************************************************************** 3 | # 4 | # Note: This file has been deprecated and exists for backward compatibility. 5 | # Please use package.json to add dependencies to the Node modules 6 | # your application requires. 7 | # 8 | # *************************************************************************** 9 | # 10 | 11 | # 12 | # For a list of globally installed modules - see file: npm_global_module_list. 13 | # 14 | -------------------------------------------------------------------------------- /jmeter/JMeter.md: -------------------------------------------------------------------------------- 1 | # JMeter 2 | 3 | ** Setup for recording 4 | [https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.pdf](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.pdf) 5 | 6 | * start Hapi on port 5000 7 | 8 | * **HTTP Request Defaults** 9 | * IP: 127.0.0.1 10 | * Port: 5000 11 | * Protocol: http 12 | 13 | * **Recording Controller** 14 | 15 | * Workbench 16 | * Test Script Recorder 17 | * Global Settings: 8080 18 | * Target Controller: Use Recording Controller 19 | * Add "View Results Tree" 20 | 21 | * Command line 22 | * export http_proxy=http://localhost:8080/ 23 | * curl --data "username=barton&password=Passw0rd$" http://127.0.0.1/account/login 24 | -------------------------------------------------------------------------------- /jmeter/snowflake.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | stopthread 16 | 17 | false 18 | -1 19 | 20 | 15 21 | 15 22 | 1451768302000 23 | 1451768302000 24 | false 25 | 26 | 27 | 28 | 29 | 30 | , 31 | 32 | snowflakeUsers.csv 33 | false 34 | false 35 | shareMode.all 36 | false 37 | username,password 38 | 39 | 40 | 41 | 42 | 43 | 44 | mysnowflake-bartonhammond.rhcloud.com 45 | 46 | 47 | 48 | https 49 | 50 | 51 | Java 52 | 4 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | false 62 | username 63 | ${username} 64 | = 65 | true 66 | 67 | 68 | false 69 | password 70 | ${password} 71 | = 72 | true 73 | 74 | 75 | false 76 | email 77 | ${username}@foobar.com 78 | = 79 | true 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | /account/register 90 | POST 91 | true 92 | false 93 | true 94 | false 95 | false 96 | 97 | 98 | 99 | 100 | 101 | 102 | Content-Type 103 | application/x-www-form-urlencoded 104 | 105 | 106 | User-Agent 107 | curl/7.43.0 108 | 109 | 110 | Accept 111 | */* 112 | 113 | 114 | 115 | 116 | 117 | 1000 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | false 126 | username 127 | ${username} 128 | = 129 | true 130 | 131 | 132 | false 133 | password 134 | ${password} 135 | = 136 | true 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | /account/login 147 | POST 148 | true 149 | false 150 | true 151 | false 152 | false 153 | 154 | 155 | 156 | 157 | sessionToken 158 | $.sessionToken 159 | 160 | 161 | BODY 162 | 163 | 164 | 165 | 1000 166 | 167 | 168 | 169 | 170 | 171 | 172 | Content-Type 173 | application/x-www-form-urlencoded 174 | 175 | 176 | User-Agent 177 | curl/7.43.0 178 | 179 | 180 | Accept 181 | */* 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | /restricted 197 | GET 198 | true 199 | false 200 | true 201 | false 202 | true 203 | false 204 | 205 | 206 | 207 | 208 | 209 | 210 | User-Agent 211 | curl/7.43.0 212 | 213 | 214 | Accept 215 | */* 216 | 217 | 218 | Authorization 219 | Bearer ${sessionToken} 220 | 221 | 222 | 223 | 224 | 225 | 1000 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | /account/profile/me 240 | GET 241 | true 242 | false 243 | true 244 | false 245 | false 246 | 247 | 248 | 249 | 250 | 251 | 252 | User-Agent 253 | curl/7.43.0 254 | 255 | 256 | Accept 257 | */* 258 | 259 | 260 | Authorization 261 | Bearer ${sessionToken} 262 | 263 | 264 | 265 | 266 | 267 | objectId 268 | $.objectId 269 | 270 | 271 | BODY 272 | 273 | 274 | 275 | 1000 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | false 284 | username 285 | ${username} 286 | = 287 | true 288 | 289 | 290 | false 291 | email 292 | ${username}x@foobar.com 293 | = 294 | true 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | /account/profile/${objectId} 305 | POST 306 | true 307 | false 308 | true 309 | false 310 | false 311 | 312 | Changes email but not username 313 | 314 | 315 | 316 | 317 | 318 | User-Agent 319 | curl/7.43.0 320 | 321 | 322 | Accept 323 | */* 324 | 325 | 326 | Authorization 327 | Bearer ${sessionToken} 328 | 329 | 330 | 331 | 332 | 333 | 1000 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | false 342 | {} 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | /account/logout 355 | POST 356 | true 357 | false 358 | true 359 | false 360 | false 361 | 362 | 363 | 364 | 365 | 366 | 367 | User-Agent 368 | curl/7.43.0 369 | 370 | 371 | Accept 372 | */* 373 | 374 | 375 | Authorization 376 | Bearer ${sessionToken} 377 | 378 | 379 | 380 | 381 | 382 | 1000 383 | 384 | 385 | 386 | 387 | false 388 | 389 | saveConfig 390 | 391 | 392 | true 393 | true 394 | true 395 | 396 | true 397 | true 398 | true 399 | true 400 | false 401 | true 402 | true 403 | false 404 | false 405 | false 406 | false 407 | false 408 | false 409 | false 410 | false 411 | 0 412 | true 413 | true 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | true 424 | 425 | 426 | 427 | 8080 428 | 429 | 430 | true 431 | 0 432 | false 433 | 434 | false 435 | true 436 | true 437 | false 438 | true 439 | false 440 | 441 | 442 | 443 | 444 | 445 | false 446 | 447 | saveConfig 448 | 449 | 450 | true 451 | true 452 | true 453 | 454 | true 455 | true 456 | true 457 | true 458 | false 459 | true 460 | true 461 | false 462 | false 463 | false 464 | false 465 | false 466 | false 467 | false 468 | false 469 | 0 470 | true 471 | true 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | -------------------------------------------------------------------------------- /jmeter/snowflakeUsers.csv: -------------------------------------------------------------------------------- 1 | 0abcdef0,Passw0rd$ 2 | 0abcdef1,Passw0rd$ 3 | 0abcdef2,Passw0rd$ 4 | 0abcdef3,Passw0rd$ 5 | 0abcdef4,Passw0rd$ 6 | 0abcdef5,Passw0rd$ 7 | 0abcdef6,Passw0rd$ 8 | 0abcdef7,Passw0rd$ 9 | 0abcdef8,Passw0rd$ 10 | 0abcdef9,Passw0rd$ 11 | 0bbcdef0,Passw0rd$ 12 | 0bbcdef1,Passw0rd$ 13 | 0bbcdef2,Passw0rd$ 14 | 0bbcdef3,Passw0rd$ 15 | 0bbcdef4,Passw0rd$ 16 | 0bbcdef5,Passw0rd$ 17 | 0bbcdef6,Passw0rd$ 18 | 0bbcdef7,Passw0rd$ 19 | 0bbcdef8,Passw0rd$ 20 | 0bbcdef9,Passw0rd$ 21 | 0cbcdef0,Passw0rd$ 22 | 0cbcdef1,Passw0rd$ 23 | 0cbcdef2,Passw0rd$ 24 | 0cbcdef3,Passw0rd$ 25 | 0cbcdef4,Passw0rd$ 26 | 0cbcdef5,Passw0rd$ 27 | 0cbcdef6,Passw0rd$ 28 | 0cbcdef7,Passw0rd$ 29 | 0cbcdef8,Passw0rd$ 30 | 0cbcdef9,Passw0rd$ 31 | 0dbcdef0,Passw0rd$ 32 | 0dbcdef1,Passw0rd$ 33 | 0dbcdef2,Passw0rd$ 34 | 0dbcdef3,Passw0rd$ 35 | 0dbcdef4,Passw0rd$ 36 | 0dbcdef5,Passw0rd$ 37 | 0dbcdef6,Passw0rd$ 38 | 0dbcdef7,Passw0rd$ 39 | 0dbcdef8,Passw0rd$ 40 | 0dbcdef9,Passw0rd$ 41 | 0dcbaef0,Passw0rd$ 42 | 0dcbaef1,Passw0rd$ 43 | 0dcbaef2,Passw0rd$ 44 | 0dcbaef3,Passw0rd$ 45 | 0dcbaef4,Passw0rd$ 46 | 0dcbaef5,Passw0rd$ 47 | 0dcbaef6,Passw0rd$ 48 | 0dcbaef7,Passw0rd$ 49 | 0dcbaef8,Passw0rd$ 50 | 0dcbaef9,Passw0rd$ 51 | 0dcbfea0,Passw0rd$ 52 | 0dcbfea1,Passw0rd$ 53 | 0dcbfea2,Passw0rd$ 54 | 0dcbfea3,Passw0rd$ 55 | 0dcbfea4,Passw0rd$ 56 | 0dcbfea5,Passw0rd$ 57 | 0dcbfea6,Passw0rd$ 58 | 0dcbfea7,Passw0rd$ 59 | 0dcbfea8,Passw0rd$ 60 | 0dcbfea9,Passw0rd$ 61 | 0ebcdef0,Passw0rd$ 62 | 0ebcdef1,Passw0rd$ 63 | 0ebcdef2,Passw0rd$ 64 | 0ebcdef3,Passw0rd$ 65 | 0ebcdef4,Passw0rd$ 66 | 0ebcdef5,Passw0rd$ 67 | 0ebcdef6,Passw0rd$ 68 | 0ebcdef7,Passw0rd$ 69 | 0ebcdef8,Passw0rd$ 70 | 0ebcdef9,Passw0rd$ 71 | 0fbcdef0,Passw0rd$ 72 | 0fbcdef1,Passw0rd$ 73 | 0fbcdef2,Passw0rd$ 74 | 0fbcdef3,Passw0rd$ 75 | 0fbcdef4,Passw0rd$ 76 | 0fbcdef5,Passw0rd$ 77 | 0fbcdef6,Passw0rd$ 78 | 0fbcdef7,Passw0rd$ 79 | 0fbcdef8,Passw0rd$ 80 | 0fbcdef9,Passw0rd$ 81 | 0fcbaef0,Passw0rd$ 82 | 0fcbaef1,Passw0rd$ 83 | 0fcbaef2,Passw0rd$ 84 | 0fcbaef3,Passw0rd$ 85 | 0fcbaef4,Passw0rd$ 86 | 0fcbaef5,Passw0rd$ 87 | 0fcbaef6,Passw0rd$ 88 | 0fcbaef7,Passw0rd$ 89 | 0fcbaef8,Passw0rd$ 90 | 0fcbaef9,Passw0rd$ 91 | 0fcbfea0,Passw0rd$ 92 | 0fcbfea1,Passw0rd$ 93 | 0fcbfea2,Passw0rd$ 94 | 0fcbfea3,Passw0rd$ 95 | 0fcbfea4,Passw0rd$ 96 | 0fcbfea5,Passw0rd$ 97 | 0fcbfea6,Passw0rd$ 98 | 0fcbfea7,Passw0rd$ 99 | 0fcbfea8,Passw0rd$ 100 | 0fcbfea9,Passw0rd$ 101 | 0hbcdef0,Passw0rd$ 102 | 0hbcdef1,Passw0rd$ 103 | 0hbcdef2,Passw0rd$ 104 | 0hbcdef3,Passw0rd$ 105 | 0hbcdef4,Passw0rd$ 106 | 0hbcdef5,Passw0rd$ 107 | 0hbcdef6,Passw0rd$ 108 | 0hbcdef7,Passw0rd$ 109 | 0hbcdef8,Passw0rd$ 110 | 0hbcdef9,Passw0rd$ 111 | 0hcbaef0,Passw0rd$ 112 | 0hcbaef1,Passw0rd$ 113 | 0hcbaef2,Passw0rd$ 114 | 0hcbaef3,Passw0rd$ 115 | 0hcbaef4,Passw0rd$ 116 | 0hcbaef5,Passw0rd$ 117 | 0hcbaef6,Passw0rd$ 118 | 0hcbaef7,Passw0rd$ 119 | 0hcbaef8,Passw0rd$ 120 | 0hcbaef9,Passw0rd$ 121 | 0hcbfea0,Passw0rd$ 122 | 0hcbfea1,Passw0rd$ 123 | 0hcbfea2,Passw0rd$ 124 | 0hcbfea3,Passw0rd$ 125 | 0hcbfea4,Passw0rd$ 126 | 0hcbfea5,Passw0rd$ 127 | 0hcbfea6,Passw0rd$ 128 | 0hcbfea7,Passw0rd$ 129 | 0hcbfea8,Passw0rd$ 130 | 0hcbfea9,Passw0rd$ 131 | 0ibcdef0,Passw0rd$ 132 | 0ibcdef1,Passw0rd$ 133 | 0ibcdef2,Passw0rd$ 134 | 0ibcdef3,Passw0rd$ 135 | 0ibcdef4,Passw0rd$ 136 | 0ibcdef5,Passw0rd$ 137 | 0ibcdef6,Passw0rd$ 138 | 0ibcdef7,Passw0rd$ 139 | 0ibcdef8,Passw0rd$ 140 | 0ibcdef9,Passw0rd$ 141 | 0icbaef0,Passw0rd$ 142 | 0icbaef1,Passw0rd$ 143 | 0icbaef2,Passw0rd$ 144 | 0icbaef3,Passw0rd$ 145 | 0icbaef4,Passw0rd$ 146 | 0icbaef5,Passw0rd$ 147 | 0icbaef6,Passw0rd$ 148 | 0icbaef7,Passw0rd$ 149 | 0icbaef8,Passw0rd$ 150 | 0icbaef9,Passw0rd$ 151 | 0icbfea0,Passw0rd$ 152 | 0icbfea1,Passw0rd$ 153 | 0icbfea2,Passw0rd$ 154 | 0icbfea3,Passw0rd$ 155 | 0icbfea4,Passw0rd$ 156 | 0icbfea5,Passw0rd$ 157 | 0icbfea6,Passw0rd$ 158 | 0icbfea7,Passw0rd$ 159 | 0icbfea8,Passw0rd$ 160 | 0icbfea9,Passw0rd$ 161 | 0jbcdef0,Passw0rd$ 162 | 0jbcdef1,Passw0rd$ 163 | 0jbcdef2,Passw0rd$ 164 | 0jbcdef3,Passw0rd$ 165 | 0jbcdef4,Passw0rd$ 166 | 0jbcdef5,Passw0rd$ 167 | 0jbcdef6,Passw0rd$ 168 | 0jbcdef7,Passw0rd$ 169 | 0jbcdef8,Passw0rd$ 170 | 0jbcdef9,Passw0rd$ 171 | 0jcbaef0,Passw0rd$ 172 | 0jcbaef1,Passw0rd$ 173 | 0jcbaef2,Passw0rd$ 174 | 0jcbaef3,Passw0rd$ 175 | 0jcbaef4,Passw0rd$ 176 | 0jcbaef5,Passw0rd$ 177 | 0jcbaef6,Passw0rd$ 178 | 0jcbaef7,Passw0rd$ 179 | 0jcbaef8,Passw0rd$ 180 | 0jcbaef9,Passw0rd$ 181 | 0jcbfea0,Passw0rd$ 182 | 0jcbfea1,Passw0rd$ 183 | 0jcbfea2,Passw0rd$ 184 | 0jcbfea3,Passw0rd$ 185 | 0jcbfea4,Passw0rd$ 186 | 0jcbfea5,Passw0rd$ 187 | 0jcbfea6,Passw0rd$ 188 | 0jcbfea7,Passw0rd$ 189 | 0jcbfea8,Passw0rd$ 190 | 0jcbfea9,Passw0rd$ 191 | 1abcdef0,Passw0rd$ 192 | 1abcdef1,Passw0rd$ 193 | 1abcdef2,Passw0rd$ 194 | 1abcdef3,Passw0rd$ 195 | 1abcdef4,Passw0rd$ 196 | 1abcdef5,Passw0rd$ 197 | 1abcdef6,Passw0rd$ 198 | 1abcdef7,Passw0rd$ 199 | 1abcdef8,Passw0rd$ 200 | 1abcdef9,Passw0rd$ 201 | 1bbcdef0,Passw0rd$ 202 | 1bbcdef1,Passw0rd$ 203 | 1bbcdef2,Passw0rd$ 204 | 1bbcdef3,Passw0rd$ 205 | 1bbcdef4,Passw0rd$ 206 | 1bbcdef5,Passw0rd$ 207 | 1bbcdef6,Passw0rd$ 208 | 1bbcdef7,Passw0rd$ 209 | 1bbcdef8,Passw0rd$ 210 | 1bbcdef9,Passw0rd$ 211 | 1cbcdef0,Passw0rd$ 212 | 1cbcdef1,Passw0rd$ 213 | 1cbcdef2,Passw0rd$ 214 | 1cbcdef3,Passw0rd$ 215 | 1cbcdef4,Passw0rd$ 216 | 1cbcdef5,Passw0rd$ 217 | 1cbcdef6,Passw0rd$ 218 | 1cbcdef7,Passw0rd$ 219 | 1cbcdef8,Passw0rd$ 220 | 1cbcdef9,Passw0rd$ 221 | 1dbcdef0,Passw0rd$ 222 | 1dbcdef1,Passw0rd$ 223 | 1dbcdef2,Passw0rd$ 224 | 1dbcdef3,Passw0rd$ 225 | 1dbcdef4,Passw0rd$ 226 | 1dbcdef5,Passw0rd$ 227 | 1dbcdef6,Passw0rd$ 228 | 1dbcdef7,Passw0rd$ 229 | 1dbcdef8,Passw0rd$ 230 | 1dbcdef9,Passw0rd$ 231 | 1dcbaef0,Passw0rd$ 232 | 1dcbaef1,Passw0rd$ 233 | 1dcbaef2,Passw0rd$ 234 | 1dcbaef3,Passw0rd$ 235 | 1dcbaef4,Passw0rd$ 236 | 1dcbaef5,Passw0rd$ 237 | 1dcbaef6,Passw0rd$ 238 | 1dcbaef7,Passw0rd$ 239 | 1dcbaef8,Passw0rd$ 240 | 1dcbaef9,Passw0rd$ 241 | 1dcbfea0,Passw0rd$ 242 | 1dcbfea1,Passw0rd$ 243 | 1dcbfea2,Passw0rd$ 244 | 1dcbfea3,Passw0rd$ 245 | 1dcbfea4,Passw0rd$ 246 | 1dcbfea5,Passw0rd$ 247 | 1dcbfea6,Passw0rd$ 248 | 1dcbfea7,Passw0rd$ 249 | 1dcbfea8,Passw0rd$ 250 | 1dcbfea9,Passw0rd$ 251 | 1ebcdef0,Passw0rd$ 252 | 1ebcdef1,Passw0rd$ 253 | 1ebcdef2,Passw0rd$ 254 | 1ebcdef3,Passw0rd$ 255 | 1ebcdef4,Passw0rd$ 256 | 1ebcdef5,Passw0rd$ 257 | 1ebcdef6,Passw0rd$ 258 | 1ebcdef7,Passw0rd$ 259 | 1ebcdef8,Passw0rd$ 260 | 1ebcdef9,Passw0rd$ 261 | 1fbcdef0,Passw0rd$ 262 | 1fbcdef1,Passw0rd$ 263 | 1fbcdef2,Passw0rd$ 264 | 1fbcdef3,Passw0rd$ 265 | 1fbcdef4,Passw0rd$ 266 | 1fbcdef5,Passw0rd$ 267 | 1fbcdef6,Passw0rd$ 268 | 1fbcdef7,Passw0rd$ 269 | 1fbcdef8,Passw0rd$ 270 | 1fbcdef9,Passw0rd$ 271 | 1fcbaef0,Passw0rd$ 272 | 1fcbaef1,Passw0rd$ 273 | 1fcbaef2,Passw0rd$ 274 | 1fcbaef3,Passw0rd$ 275 | 1fcbaef4,Passw0rd$ 276 | 1fcbaef5,Passw0rd$ 277 | 1fcbaef6,Passw0rd$ 278 | 1fcbaef7,Passw0rd$ 279 | 1fcbaef8,Passw0rd$ 280 | 1fcbaef9,Passw0rd$ 281 | 1fcbfea0,Passw0rd$ 282 | 1fcbfea1,Passw0rd$ 283 | 1fcbfea2,Passw0rd$ 284 | 1fcbfea3,Passw0rd$ 285 | 1fcbfea4,Passw0rd$ 286 | 1fcbfea5,Passw0rd$ 287 | 1fcbfea6,Passw0rd$ 288 | 1fcbfea7,Passw0rd$ 289 | 1fcbfea8,Passw0rd$ 290 | 1fcbfea9,Passw0rd$ 291 | 1hbcdef0,Passw0rd$ 292 | 1hbcdef1,Passw0rd$ 293 | 1hbcdef2,Passw0rd$ 294 | 1hbcdef3,Passw0rd$ 295 | 1hbcdef4,Passw0rd$ 296 | 1hbcdef5,Passw0rd$ 297 | 1hbcdef6,Passw0rd$ 298 | 1hbcdef7,Passw0rd$ 299 | 1hbcdef8,Passw0rd$ 300 | 1hbcdef9,Passw0rd$ 301 | 1hcbaef0,Passw0rd$ 302 | 1hcbaef1,Passw0rd$ 303 | 1hcbaef2,Passw0rd$ 304 | 1hcbaef3,Passw0rd$ 305 | 1hcbaef4,Passw0rd$ 306 | 1hcbaef5,Passw0rd$ 307 | 1hcbaef6,Passw0rd$ 308 | 1hcbaef7,Passw0rd$ 309 | 1hcbaef8,Passw0rd$ 310 | 1hcbaef9,Passw0rd$ 311 | 1hcbfea0,Passw0rd$ 312 | 1hcbfea1,Passw0rd$ 313 | 1hcbfea2,Passw0rd$ 314 | 1hcbfea3,Passw0rd$ 315 | 1hcbfea4,Passw0rd$ 316 | 1hcbfea5,Passw0rd$ 317 | 1hcbfea6,Passw0rd$ 318 | 1hcbfea7,Passw0rd$ 319 | 1hcbfea8,Passw0rd$ 320 | 1hcbfea9,Passw0rd$ 321 | 1ibcdef0,Passw0rd$ 322 | 1ibcdef1,Passw0rd$ 323 | 1ibcdef2,Passw0rd$ 324 | 1ibcdef3,Passw0rd$ 325 | 1ibcdef4,Passw0rd$ 326 | 1ibcdef5,Passw0rd$ 327 | 1ibcdef6,Passw0rd$ 328 | 1ibcdef7,Passw0rd$ 329 | 1ibcdef8,Passw0rd$ 330 | 1ibcdef9,Passw0rd$ 331 | 1icbaef0,Passw0rd$ 332 | 1icbaef1,Passw0rd$ 333 | 1icbaef2,Passw0rd$ 334 | 1icbaef3,Passw0rd$ 335 | 1icbaef4,Passw0rd$ 336 | 1icbaef5,Passw0rd$ 337 | 1icbaef6,Passw0rd$ 338 | 1icbaef7,Passw0rd$ 339 | 1icbaef8,Passw0rd$ 340 | 1icbaef9,Passw0rd$ 341 | 1icbfea0,Passw0rd$ 342 | 1icbfea1,Passw0rd$ 343 | 1icbfea2,Passw0rd$ 344 | 1icbfea3,Passw0rd$ 345 | 1icbfea4,Passw0rd$ 346 | 1icbfea5,Passw0rd$ 347 | 1icbfea6,Passw0rd$ 348 | 1icbfea7,Passw0rd$ 349 | 1icbfea8,Passw0rd$ 350 | 1icbfea9,Passw0rd$ 351 | 1jbcdef0,Passw0rd$ 352 | 1jbcdef1,Passw0rd$ 353 | 1jbcdef2,Passw0rd$ 354 | 1jbcdef3,Passw0rd$ 355 | 1jbcdef4,Passw0rd$ 356 | 1jbcdef5,Passw0rd$ 357 | 1jbcdef6,Passw0rd$ 358 | 1jbcdef7,Passw0rd$ 359 | 1jbcdef8,Passw0rd$ 360 | 1jbcdef9,Passw0rd$ 361 | 1jcbaef0,Passw0rd$ 362 | 1jcbaef1,Passw0rd$ 363 | 1jcbaef2,Passw0rd$ 364 | 1jcbaef3,Passw0rd$ 365 | 1jcbaef4,Passw0rd$ 366 | 1jcbaef5,Passw0rd$ 367 | 1jcbaef6,Passw0rd$ 368 | 1jcbaef7,Passw0rd$ 369 | 1jcbaef8,Passw0rd$ 370 | 1jcbaef9,Passw0rd$ 371 | 1jcbfea0,Passw0rd$ 372 | 1jcbfea1,Passw0rd$ 373 | 1jcbfea2,Passw0rd$ 374 | 1jcbfea3,Passw0rd$ 375 | 1jcbfea4,Passw0rd$ 376 | 1jcbfea5,Passw0rd$ 377 | 1jcbfea6,Passw0rd$ 378 | 1jcbfea7,Passw0rd$ 379 | 1jcbfea8,Passw0rd$ 380 | 1jcbfea9,Passw0rd$ 381 | 2abcdef0,Passw0rd$ 382 | 2abcdef1,Passw0rd$ 383 | 2abcdef2,Passw0rd$ 384 | 2abcdef3,Passw0rd$ 385 | 2abcdef4,Passw0rd$ 386 | 2abcdef5,Passw0rd$ 387 | 2abcdef6,Passw0rd$ 388 | 2abcdef7,Passw0rd$ 389 | 2abcdef8,Passw0rd$ 390 | 2abcdef9,Passw0rd$ 391 | 2bbcdef0,Passw0rd$ 392 | 2bbcdef1,Passw0rd$ 393 | 2bbcdef2,Passw0rd$ 394 | 2bbcdef3,Passw0rd$ 395 | 2bbcdef4,Passw0rd$ 396 | 2bbcdef5,Passw0rd$ 397 | 2bbcdef6,Passw0rd$ 398 | 2bbcdef7,Passw0rd$ 399 | 2bbcdef8,Passw0rd$ 400 | 2bbcdef9,Passw0rd$ 401 | 2cbcdef0,Passw0rd$ 402 | 2cbcdef1,Passw0rd$ 403 | 2cbcdef2,Passw0rd$ 404 | 2cbcdef3,Passw0rd$ 405 | 2cbcdef4,Passw0rd$ 406 | 2cbcdef5,Passw0rd$ 407 | 2cbcdef6,Passw0rd$ 408 | 2cbcdef7,Passw0rd$ 409 | 2cbcdef8,Passw0rd$ 410 | 2cbcdef9,Passw0rd$ 411 | 2dbcdef0,Passw0rd$ 412 | 2dbcdef1,Passw0rd$ 413 | 2dbcdef2,Passw0rd$ 414 | 2dbcdef3,Passw0rd$ 415 | 2dbcdef4,Passw0rd$ 416 | 2dbcdef5,Passw0rd$ 417 | 2dbcdef6,Passw0rd$ 418 | 2dbcdef7,Passw0rd$ 419 | 2dbcdef8,Passw0rd$ 420 | 2dbcdef9,Passw0rd$ 421 | 2dcbaef0,Passw0rd$ 422 | 2dcbaef1,Passw0rd$ 423 | 2dcbaef2,Passw0rd$ 424 | 2dcbaef3,Passw0rd$ 425 | 2dcbaef4,Passw0rd$ 426 | 2dcbaef5,Passw0rd$ 427 | 2dcbaef6,Passw0rd$ 428 | 2dcbaef7,Passw0rd$ 429 | 2dcbaef8,Passw0rd$ 430 | 2dcbaef9,Passw0rd$ 431 | 2dcbfea0,Passw0rd$ 432 | 2dcbfea1,Passw0rd$ 433 | 2dcbfea2,Passw0rd$ 434 | 2dcbfea3,Passw0rd$ 435 | 2dcbfea4,Passw0rd$ 436 | 2dcbfea5,Passw0rd$ 437 | 2dcbfea6,Passw0rd$ 438 | 2dcbfea7,Passw0rd$ 439 | 2dcbfea8,Passw0rd$ 440 | 2dcbfea9,Passw0rd$ 441 | 2ebcdef0,Passw0rd$ 442 | 2ebcdef1,Passw0rd$ 443 | 2ebcdef2,Passw0rd$ 444 | 2ebcdef3,Passw0rd$ 445 | 2ebcdef4,Passw0rd$ 446 | 2ebcdef5,Passw0rd$ 447 | 2ebcdef6,Passw0rd$ 448 | 2ebcdef7,Passw0rd$ 449 | 2ebcdef8,Passw0rd$ 450 | 2ebcdef9,Passw0rd$ 451 | 2fbcdef0,Passw0rd$ 452 | 2fbcdef1,Passw0rd$ 453 | 2fbcdef2,Passw0rd$ 454 | 2fbcdef3,Passw0rd$ 455 | 2fbcdef4,Passw0rd$ 456 | 2fbcdef5,Passw0rd$ 457 | 2fbcdef6,Passw0rd$ 458 | 2fbcdef7,Passw0rd$ 459 | 2fbcdef8,Passw0rd$ 460 | 2fbcdef9,Passw0rd$ 461 | 2fcbaef0,Passw0rd$ 462 | 2fcbaef1,Passw0rd$ 463 | 2fcbaef2,Passw0rd$ 464 | 2fcbaef3,Passw0rd$ 465 | 2fcbaef4,Passw0rd$ 466 | 2fcbaef5,Passw0rd$ 467 | 2fcbaef6,Passw0rd$ 468 | 2fcbaef7,Passw0rd$ 469 | 2fcbaef8,Passw0rd$ 470 | 2fcbaef9,Passw0rd$ 471 | 2fcbfea0,Passw0rd$ 472 | 2fcbfea1,Passw0rd$ 473 | 2fcbfea2,Passw0rd$ 474 | 2fcbfea3,Passw0rd$ 475 | 2fcbfea4,Passw0rd$ 476 | 2fcbfea5,Passw0rd$ 477 | 2fcbfea6,Passw0rd$ 478 | 2fcbfea7,Passw0rd$ 479 | 2fcbfea8,Passw0rd$ 480 | 2fcbfea9,Passw0rd$ 481 | 2hbcdef0,Passw0rd$ 482 | 2hbcdef1,Passw0rd$ 483 | 2hbcdef2,Passw0rd$ 484 | 2hbcdef3,Passw0rd$ 485 | 2hbcdef4,Passw0rd$ 486 | 2hbcdef5,Passw0rd$ 487 | 2hbcdef6,Passw0rd$ 488 | 2hbcdef7,Passw0rd$ 489 | 2hbcdef8,Passw0rd$ 490 | 2hbcdef9,Passw0rd$ 491 | 2hcbaef0,Passw0rd$ 492 | 2hcbaef1,Passw0rd$ 493 | 2hcbaef2,Passw0rd$ 494 | 2hcbaef3,Passw0rd$ 495 | 2hcbaef4,Passw0rd$ 496 | 2hcbaef5,Passw0rd$ 497 | 2hcbaef6,Passw0rd$ 498 | 2hcbaef7,Passw0rd$ 499 | 2hcbaef8,Passw0rd$ 500 | 2hcbaef9,Passw0rd$ 501 | 2hcbfea0,Passw0rd$ 502 | 2hcbfea1,Passw0rd$ 503 | 2hcbfea2,Passw0rd$ 504 | 2hcbfea3,Passw0rd$ 505 | 2hcbfea4,Passw0rd$ 506 | 2hcbfea5,Passw0rd$ 507 | 2hcbfea6,Passw0rd$ 508 | 2hcbfea7,Passw0rd$ 509 | 2hcbfea8,Passw0rd$ 510 | 2hcbfea9,Passw0rd$ 511 | 2ibcdef0,Passw0rd$ 512 | 2ibcdef1,Passw0rd$ 513 | 2ibcdef2,Passw0rd$ 514 | 2ibcdef3,Passw0rd$ 515 | 2ibcdef4,Passw0rd$ 516 | 2ibcdef5,Passw0rd$ 517 | 2ibcdef6,Passw0rd$ 518 | 2ibcdef7,Passw0rd$ 519 | 2ibcdef8,Passw0rd$ 520 | 2ibcdef9,Passw0rd$ 521 | 2icbaef0,Passw0rd$ 522 | 2icbaef1,Passw0rd$ 523 | 2icbaef2,Passw0rd$ 524 | 2icbaef3,Passw0rd$ 525 | 2icbaef4,Passw0rd$ 526 | 2icbaef5,Passw0rd$ 527 | 2icbaef6,Passw0rd$ 528 | 2icbaef7,Passw0rd$ 529 | 2icbaef8,Passw0rd$ 530 | 2icbaef9,Passw0rd$ 531 | 2icbfea0,Passw0rd$ 532 | 2icbfea1,Passw0rd$ 533 | 2icbfea2,Passw0rd$ 534 | 2icbfea3,Passw0rd$ 535 | 2icbfea4,Passw0rd$ 536 | 2icbfea5,Passw0rd$ 537 | 2icbfea6,Passw0rd$ 538 | 2icbfea7,Passw0rd$ 539 | 2icbfea8,Passw0rd$ 540 | 2icbfea9,Passw0rd$ 541 | 2jbcdef0,Passw0rd$ 542 | 2jbcdef1,Passw0rd$ 543 | 2jbcdef2,Passw0rd$ 544 | 2jbcdef3,Passw0rd$ 545 | 2jbcdef4,Passw0rd$ 546 | 2jbcdef5,Passw0rd$ 547 | 2jbcdef6,Passw0rd$ 548 | 2jbcdef7,Passw0rd$ 549 | 2jbcdef8,Passw0rd$ 550 | 2jbcdef9,Passw0rd$ 551 | 2jcbaef0,Passw0rd$ 552 | 2jcbaef1,Passw0rd$ 553 | 2jcbaef2,Passw0rd$ 554 | 2jcbaef3,Passw0rd$ 555 | 2jcbaef4,Passw0rd$ 556 | 2jcbaef5,Passw0rd$ 557 | 2jcbaef6,Passw0rd$ 558 | 2jcbaef7,Passw0rd$ 559 | 2jcbaef8,Passw0rd$ 560 | 2jcbaef9,Passw0rd$ 561 | 2jcbfea0,Passw0rd$ 562 | 2jcbfea1,Passw0rd$ 563 | 2jcbfea2,Passw0rd$ 564 | 2jcbfea3,Passw0rd$ 565 | 2jcbfea4,Passw0rd$ 566 | 2jcbfea5,Passw0rd$ 567 | 2jcbfea6,Passw0rd$ 568 | 2jcbfea7,Passw0rd$ 569 | 2jcbfea8,Passw0rd$ 570 | 2jcbfea9,Passw0rd$ 571 | 3abcdef0,Passw0rd$ 572 | 3abcdef1,Passw0rd$ 573 | 3abcdef2,Passw0rd$ 574 | 3abcdef3,Passw0rd$ 575 | 3abcdef4,Passw0rd$ 576 | 3abcdef5,Passw0rd$ 577 | 3abcdef6,Passw0rd$ 578 | 3abcdef7,Passw0rd$ 579 | 3abcdef8,Passw0rd$ 580 | 3abcdef9,Passw0rd$ 581 | 3bbcdef0,Passw0rd$ 582 | 3bbcdef1,Passw0rd$ 583 | 3bbcdef2,Passw0rd$ 584 | 3bbcdef3,Passw0rd$ 585 | 3bbcdef4,Passw0rd$ 586 | 3bbcdef5,Passw0rd$ 587 | 3bbcdef6,Passw0rd$ 588 | 3bbcdef7,Passw0rd$ 589 | 3bbcdef8,Passw0rd$ 590 | 3bbcdef9,Passw0rd$ 591 | 3cbcdef0,Passw0rd$ 592 | 3cbcdef1,Passw0rd$ 593 | 3cbcdef2,Passw0rd$ 594 | 3cbcdef3,Passw0rd$ 595 | 3cbcdef4,Passw0rd$ 596 | 3cbcdef5,Passw0rd$ 597 | 3cbcdef6,Passw0rd$ 598 | 3cbcdef7,Passw0rd$ 599 | 3cbcdef8,Passw0rd$ 600 | 3cbcdef9,Passw0rd$ 601 | 3dbcdef0,Passw0rd$ 602 | 3dbcdef1,Passw0rd$ 603 | 3dbcdef2,Passw0rd$ 604 | 3dbcdef3,Passw0rd$ 605 | 3dbcdef4,Passw0rd$ 606 | 3dbcdef5,Passw0rd$ 607 | 3dbcdef6,Passw0rd$ 608 | 3dbcdef7,Passw0rd$ 609 | 3dbcdef8,Passw0rd$ 610 | 3dbcdef9,Passw0rd$ 611 | 3dcbaef0,Passw0rd$ 612 | 3dcbaef1,Passw0rd$ 613 | 3dcbaef2,Passw0rd$ 614 | 3dcbaef3,Passw0rd$ 615 | 3dcbaef4,Passw0rd$ 616 | 3dcbaef5,Passw0rd$ 617 | 3dcbaef6,Passw0rd$ 618 | 3dcbaef7,Passw0rd$ 619 | 3dcbaef8,Passw0rd$ 620 | 3dcbaef9,Passw0rd$ 621 | 3dcbfea0,Passw0rd$ 622 | 3dcbfea1,Passw0rd$ 623 | 3dcbfea2,Passw0rd$ 624 | 3dcbfea3,Passw0rd$ 625 | 3dcbfea4,Passw0rd$ 626 | 3dcbfea5,Passw0rd$ 627 | 3dcbfea6,Passw0rd$ 628 | 3dcbfea7,Passw0rd$ 629 | 3dcbfea8,Passw0rd$ 630 | 3dcbfea9,Passw0rd$ 631 | 3ebcdef0,Passw0rd$ 632 | 3ebcdef1,Passw0rd$ 633 | 3ebcdef2,Passw0rd$ 634 | 3ebcdef3,Passw0rd$ 635 | 3ebcdef4,Passw0rd$ 636 | 3ebcdef5,Passw0rd$ 637 | 3ebcdef6,Passw0rd$ 638 | 3ebcdef7,Passw0rd$ 639 | 3ebcdef8,Passw0rd$ 640 | 3ebcdef9,Passw0rd$ 641 | 3fbcdef0,Passw0rd$ 642 | 3fbcdef1,Passw0rd$ 643 | 3fbcdef2,Passw0rd$ 644 | 3fbcdef3,Passw0rd$ 645 | 3fbcdef4,Passw0rd$ 646 | 3fbcdef5,Passw0rd$ 647 | 3fbcdef6,Passw0rd$ 648 | 3fbcdef7,Passw0rd$ 649 | 3fbcdef8,Passw0rd$ 650 | 3fbcdef9,Passw0rd$ 651 | 3fcbaef0,Passw0rd$ 652 | 3fcbaef1,Passw0rd$ 653 | 3fcbaef2,Passw0rd$ 654 | 3fcbaef3,Passw0rd$ 655 | 3fcbaef4,Passw0rd$ 656 | 3fcbaef5,Passw0rd$ 657 | 3fcbaef6,Passw0rd$ 658 | 3fcbaef7,Passw0rd$ 659 | 3fcbaef8,Passw0rd$ 660 | 3fcbaef9,Passw0rd$ 661 | 3fcbfea0,Passw0rd$ 662 | 3fcbfea1,Passw0rd$ 663 | 3fcbfea2,Passw0rd$ 664 | 3fcbfea3,Passw0rd$ 665 | 3fcbfea4,Passw0rd$ 666 | 3fcbfea5,Passw0rd$ 667 | 3fcbfea6,Passw0rd$ 668 | 3fcbfea7,Passw0rd$ 669 | 3fcbfea8,Passw0rd$ 670 | 3fcbfea9,Passw0rd$ 671 | 3hbcdef0,Passw0rd$ 672 | 3hbcdef1,Passw0rd$ 673 | 3hbcdef2,Passw0rd$ 674 | 3hbcdef3,Passw0rd$ 675 | 3hbcdef4,Passw0rd$ 676 | 3hbcdef5,Passw0rd$ 677 | 3hbcdef6,Passw0rd$ 678 | 3hbcdef7,Passw0rd$ 679 | 3hbcdef8,Passw0rd$ 680 | 3hbcdef9,Passw0rd$ 681 | 3hcbaef0,Passw0rd$ 682 | 3hcbaef1,Passw0rd$ 683 | 3hcbaef2,Passw0rd$ 684 | 3hcbaef3,Passw0rd$ 685 | 3hcbaef4,Passw0rd$ 686 | 3hcbaef5,Passw0rd$ 687 | 3hcbaef6,Passw0rd$ 688 | 3hcbaef7,Passw0rd$ 689 | 3hcbaef8,Passw0rd$ 690 | 3hcbaef9,Passw0rd$ 691 | 3hcbfea0,Passw0rd$ 692 | 3hcbfea1,Passw0rd$ 693 | 3hcbfea2,Passw0rd$ 694 | 3hcbfea3,Passw0rd$ 695 | 3hcbfea4,Passw0rd$ 696 | 3hcbfea5,Passw0rd$ 697 | 3hcbfea6,Passw0rd$ 698 | 3hcbfea7,Passw0rd$ 699 | 3hcbfea8,Passw0rd$ 700 | 3hcbfea9,Passw0rd$ 701 | 3ibcdef0,Passw0rd$ 702 | 3ibcdef1,Passw0rd$ 703 | 3ibcdef2,Passw0rd$ 704 | 3ibcdef3,Passw0rd$ 705 | 3ibcdef4,Passw0rd$ 706 | 3ibcdef5,Passw0rd$ 707 | 3ibcdef6,Passw0rd$ 708 | 3ibcdef7,Passw0rd$ 709 | 3ibcdef8,Passw0rd$ 710 | 3ibcdef9,Passw0rd$ 711 | 3icbaef0,Passw0rd$ 712 | 3icbaef1,Passw0rd$ 713 | 3icbaef2,Passw0rd$ 714 | 3icbaef3,Passw0rd$ 715 | 3icbaef4,Passw0rd$ 716 | 3icbaef5,Passw0rd$ 717 | 3icbaef6,Passw0rd$ 718 | 3icbaef7,Passw0rd$ 719 | 3icbaef8,Passw0rd$ 720 | 3icbaef9,Passw0rd$ 721 | 3icbfea0,Passw0rd$ 722 | 3icbfea1,Passw0rd$ 723 | 3icbfea2,Passw0rd$ 724 | 3icbfea3,Passw0rd$ 725 | 3icbfea4,Passw0rd$ 726 | 3icbfea5,Passw0rd$ 727 | 3icbfea6,Passw0rd$ 728 | 3icbfea7,Passw0rd$ 729 | 3icbfea8,Passw0rd$ 730 | 3icbfea9,Passw0rd$ 731 | 3jbcdef0,Passw0rd$ 732 | 3jbcdef1,Passw0rd$ 733 | 3jbcdef2,Passw0rd$ 734 | 3jbcdef3,Passw0rd$ 735 | 3jbcdef4,Passw0rd$ 736 | 3jbcdef5,Passw0rd$ 737 | 3jbcdef6,Passw0rd$ 738 | 3jbcdef7,Passw0rd$ 739 | 3jbcdef8,Passw0rd$ 740 | 3jbcdef9,Passw0rd$ 741 | 3jcbaef0,Passw0rd$ 742 | 3jcbaef1,Passw0rd$ 743 | 3jcbaef2,Passw0rd$ 744 | 3jcbaef3,Passw0rd$ 745 | 3jcbaef4,Passw0rd$ 746 | 3jcbaef5,Passw0rd$ 747 | 3jcbaef6,Passw0rd$ 748 | 3jcbaef7,Passw0rd$ 749 | 3jcbaef8,Passw0rd$ 750 | 3jcbaef9,Passw0rd$ 751 | 3jcbfea0,Passw0rd$ 752 | 3jcbfea1,Passw0rd$ 753 | 3jcbfea2,Passw0rd$ 754 | 3jcbfea3,Passw0rd$ 755 | 3jcbfea4,Passw0rd$ 756 | 3jcbfea5,Passw0rd$ 757 | 3jcbfea6,Passw0rd$ 758 | 3jcbfea7,Passw0rd$ 759 | 3jcbfea8,Passw0rd$ 760 | 3jcbfea9,Passw0rd$ 761 | abcdef0,Passw0rd$ 762 | abcdef1,Passw0rd$ 763 | abcdef2,Passw0rd$ 764 | abcdef3,Passw0rd$ 765 | abcdef4,Passw0rd$ 766 | abcdef5,Passw0rd$ 767 | abcdef6,Passw0rd$ 768 | abcdef7,Passw0rd$ 769 | abcdef8,Passw0rd$ 770 | abcdef9,Passw0rd$ 771 | bbcdef0,Passw0rd$ 772 | bbcdef1,Passw0rd$ 773 | bbcdef2,Passw0rd$ 774 | bbcdef3,Passw0rd$ 775 | bbcdef4,Passw0rd$ 776 | bbcdef5,Passw0rd$ 777 | bbcdef6,Passw0rd$ 778 | bbcdef7,Passw0rd$ 779 | bbcdef8,Passw0rd$ 780 | bbcdef9,Passw0rd$ 781 | cbcdef0,Passw0rd$ 782 | cbcdef1,Passw0rd$ 783 | cbcdef2,Passw0rd$ 784 | cbcdef3,Passw0rd$ 785 | cbcdef4,Passw0rd$ 786 | cbcdef5,Passw0rd$ 787 | cbcdef6,Passw0rd$ 788 | cbcdef7,Passw0rd$ 789 | cbcdef8,Passw0rd$ 790 | cbcdef9,Passw0rd$ 791 | dbcdef0,Passw0rd$ 792 | dbcdef1,Passw0rd$ 793 | dbcdef2,Passw0rd$ 794 | dbcdef3,Passw0rd$ 795 | dbcdef4,Passw0rd$ 796 | dbcdef5,Passw0rd$ 797 | dbcdef6,Passw0rd$ 798 | dbcdef7,Passw0rd$ 799 | dbcdef8,Passw0rd$ 800 | dbcdef9,Passw0rd$ 801 | dcbaef0,Passw0rd$ 802 | dcbaef1,Passw0rd$ 803 | dcbaef2,Passw0rd$ 804 | dcbaef3,Passw0rd$ 805 | dcbaef4,Passw0rd$ 806 | dcbaef5,Passw0rd$ 807 | dcbaef6,Passw0rd$ 808 | dcbaef7,Passw0rd$ 809 | dcbaef8,Passw0rd$ 810 | dcbaef9,Passw0rd$ 811 | dcbfea0,Passw0rd$ 812 | dcbfea1,Passw0rd$ 813 | dcbfea2,Passw0rd$ 814 | dcbfea3,Passw0rd$ 815 | dcbfea4,Passw0rd$ 816 | dcbfea5,Passw0rd$ 817 | dcbfea6,Passw0rd$ 818 | dcbfea7,Passw0rd$ 819 | dcbfea8,Passw0rd$ 820 | dcbfea9,Passw0rd$ 821 | ebcdef0,Passw0rd$ 822 | ebcdef1,Passw0rd$ 823 | ebcdef2,Passw0rd$ 824 | ebcdef3,Passw0rd$ 825 | ebcdef4,Passw0rd$ 826 | ebcdef5,Passw0rd$ 827 | ebcdef6,Passw0rd$ 828 | ebcdef7,Passw0rd$ 829 | ebcdef8,Passw0rd$ 830 | ebcdef9,Passw0rd$ 831 | fbcdef0,Passw0rd$ 832 | fbcdef1,Passw0rd$ 833 | fbcdef2,Passw0rd$ 834 | fbcdef3,Passw0rd$ 835 | fbcdef4,Passw0rd$ 836 | fbcdef5,Passw0rd$ 837 | fbcdef6,Passw0rd$ 838 | fbcdef7,Passw0rd$ 839 | fbcdef8,Passw0rd$ 840 | fbcdef9,Passw0rd$ 841 | fcbaef0,Passw0rd$ 842 | fcbaef1,Passw0rd$ 843 | fcbaef2,Passw0rd$ 844 | fcbaef3,Passw0rd$ 845 | fcbaef4,Passw0rd$ 846 | fcbaef5,Passw0rd$ 847 | fcbaef6,Passw0rd$ 848 | fcbaef7,Passw0rd$ 849 | fcbaef8,Passw0rd$ 850 | fcbaef9,Passw0rd$ 851 | fcbfea0,Passw0rd$ 852 | fcbfea1,Passw0rd$ 853 | fcbfea2,Passw0rd$ 854 | fcbfea3,Passw0rd$ 855 | fcbfea4,Passw0rd$ 856 | fcbfea5,Passw0rd$ 857 | fcbfea6,Passw0rd$ 858 | fcbfea7,Passw0rd$ 859 | fcbfea8,Passw0rd$ 860 | fcbfea9,Passw0rd$ 861 | hbcdef0,Passw0rd$ 862 | hbcdef1,Passw0rd$ 863 | hbcdef2,Passw0rd$ 864 | hbcdef3,Passw0rd$ 865 | hbcdef4,Passw0rd$ 866 | hbcdef5,Passw0rd$ 867 | hbcdef6,Passw0rd$ 868 | hbcdef7,Passw0rd$ 869 | hbcdef8,Passw0rd$ 870 | hbcdef9,Passw0rd$ 871 | hcbaef0,Passw0rd$ 872 | hcbaef1,Passw0rd$ 873 | hcbaef2,Passw0rd$ 874 | hcbaef3,Passw0rd$ 875 | hcbaef4,Passw0rd$ 876 | hcbaef5,Passw0rd$ 877 | hcbaef6,Passw0rd$ 878 | hcbaef7,Passw0rd$ 879 | hcbaef8,Passw0rd$ 880 | hcbaef9,Passw0rd$ 881 | hcbfea0,Passw0rd$ 882 | hcbfea1,Passw0rd$ 883 | hcbfea2,Passw0rd$ 884 | hcbfea3,Passw0rd$ 885 | hcbfea4,Passw0rd$ 886 | hcbfea5,Passw0rd$ 887 | hcbfea6,Passw0rd$ 888 | hcbfea7,Passw0rd$ 889 | hcbfea8,Passw0rd$ 890 | hcbfea9,Passw0rd$ 891 | ibcdef0,Passw0rd$ 892 | ibcdef1,Passw0rd$ 893 | ibcdef2,Passw0rd$ 894 | ibcdef3,Passw0rd$ 895 | ibcdef4,Passw0rd$ 896 | ibcdef5,Passw0rd$ 897 | ibcdef6,Passw0rd$ 898 | ibcdef7,Passw0rd$ 899 | ibcdef8,Passw0rd$ 900 | ibcdef9,Passw0rd$ 901 | icbaef0,Passw0rd$ 902 | icbaef1,Passw0rd$ 903 | icbaef2,Passw0rd$ 904 | icbaef3,Passw0rd$ 905 | icbaef4,Passw0rd$ 906 | icbaef5,Passw0rd$ 907 | icbaef6,Passw0rd$ 908 | icbaef7,Passw0rd$ 909 | icbaef8,Passw0rd$ 910 | icbaef9,Passw0rd$ 911 | icbfea0,Passw0rd$ 912 | icbfea1,Passw0rd$ 913 | icbfea2,Passw0rd$ 914 | icbfea3,Passw0rd$ 915 | icbfea4,Passw0rd$ 916 | icbfea5,Passw0rd$ 917 | icbfea6,Passw0rd$ 918 | icbfea7,Passw0rd$ 919 | icbfea8,Passw0rd$ 920 | icbfea9,Passw0rd$ 921 | jbcdef0,Passw0rd$ 922 | jbcdef1,Passw0rd$ 923 | jbcdef2,Passw0rd$ 924 | jbcdef3,Passw0rd$ 925 | jbcdef4,Passw0rd$ 926 | jbcdef5,Passw0rd$ 927 | jbcdef6,Passw0rd$ 928 | jbcdef7,Passw0rd$ 929 | jbcdef8,Passw0rd$ 930 | jbcdef9,Passw0rd$ 931 | jcbaef0,Passw0rd$ 932 | jcbaef1,Passw0rd$ 933 | jcbaef2,Passw0rd$ 934 | jcbaef3,Passw0rd$ 935 | jcbaef4,Passw0rd$ 936 | jcbaef5,Passw0rd$ 937 | jcbaef6,Passw0rd$ 938 | jcbaef7,Passw0rd$ 939 | jcbaef8,Passw0rd$ 940 | jcbaef9,Passw0rd$ 941 | jcbfea0,Passw0rd$ 942 | jcbfea1,Passw0rd$ 943 | jcbfea2,Passw0rd$ 944 | jcbfea3,Passw0rd$ 945 | jcbfea4,Passw0rd$ 946 | jcbfea5,Passw0rd$ 947 | jcbfea6,Passw0rd$ 948 | jcbfea7,Passw0rd$ 949 | jcbfea8,Passw0rd$ 950 | jcbfea9,Passw0rd$ 951 | z0abcdef0,Passw0rd$ 952 | z0abcdef1,Passw0rd$ 953 | z0abcdef2,Passw0rd$ 954 | z0abcdef3,Passw0rd$ 955 | z0abcdef4,Passw0rd$ 956 | z0abcdef5,Passw0rd$ 957 | z0abcdef6,Passw0rd$ 958 | z0abcdef7,Passw0rd$ 959 | z0abcdef8,Passw0rd$ 960 | z0abcdef9,Passw0rd$ 961 | z0bbcdef0,Passw0rd$ 962 | z0bbcdef1,Passw0rd$ 963 | z0bbcdef2,Passw0rd$ 964 | z0bbcdef3,Passw0rd$ 965 | z0bbcdef4,Passw0rd$ 966 | z0bbcdef5,Passw0rd$ 967 | z0bbcdef6,Passw0rd$ 968 | z0bbcdef7,Passw0rd$ 969 | z0bbcdef8,Passw0rd$ 970 | z0bbcdef9,Passw0rd$ 971 | z0cbcdef0,Passw0rd$ 972 | z0cbcdef1,Passw0rd$ 973 | z0cbcdef2,Passw0rd$ 974 | z0cbcdef3,Passw0rd$ 975 | z0cbcdef4,Passw0rd$ 976 | z0cbcdef5,Passw0rd$ 977 | z0cbcdef6,Passw0rd$ 978 | z0cbcdef7,Passw0rd$ 979 | z0cbcdef8,Passw0rd$ 980 | z0cbcdef9,Passw0rd$ 981 | z0dbcdef0,Passw0rd$ 982 | z0dbcdef1,Passw0rd$ 983 | z0dbcdef2,Passw0rd$ 984 | z0dbcdef3,Passw0rd$ 985 | z0dbcdef4,Passw0rd$ 986 | z0dbcdef5,Passw0rd$ 987 | z0dbcdef6,Passw0rd$ 988 | z0dbcdef7,Passw0rd$ 989 | z0dbcdef8,Passw0rd$ 990 | z0dbcdef9,Passw0rd$ 991 | z0dcbaef0,Passw0rd$ 992 | z0dcbaef1,Passw0rd$ 993 | z0dcbaef2,Passw0rd$ 994 | z0dcbaef3,Passw0rd$ 995 | z0dcbaef4,Passw0rd$ 996 | z0dcbaef5,Passw0rd$ 997 | z0dcbaef6,Passw0rd$ 998 | z0dcbaef7,Passw0rd$ 999 | z0dcbaef8,Passw0rd$ 1000 | z0dcbaef9,Passw0rd$ 1001 | z0dcbfea0,Passw0rd$ 1002 | z0dcbfea1,Passw0rd$ 1003 | z0dcbfea2,Passw0rd$ 1004 | z0dcbfea3,Passw0rd$ 1005 | z0dcbfea4,Passw0rd$ 1006 | z0dcbfea5,Passw0rd$ 1007 | z0dcbfea6,Passw0rd$ 1008 | z0dcbfea7,Passw0rd$ 1009 | z0dcbfea8,Passw0rd$ 1010 | z0dcbfea9,Passw0rd$ 1011 | z0ebcdef0,Passw0rd$ 1012 | z0ebcdef1,Passw0rd$ 1013 | z0ebcdef2,Passw0rd$ 1014 | z0ebcdef3,Passw0rd$ 1015 | z0ebcdef4,Passw0rd$ 1016 | z0ebcdef5,Passw0rd$ 1017 | z0ebcdef6,Passw0rd$ 1018 | z0ebcdef7,Passw0rd$ 1019 | z0ebcdef8,Passw0rd$ 1020 | z0ebcdef9,Passw0rd$ 1021 | z0fbcdef0,Passw0rd$ 1022 | z0fbcdef1,Passw0rd$ 1023 | z0fbcdef2,Passw0rd$ 1024 | z0fbcdef3,Passw0rd$ 1025 | z0fbcdef4,Passw0rd$ 1026 | z0fbcdef5,Passw0rd$ 1027 | z0fbcdef6,Passw0rd$ 1028 | z0fbcdef7,Passw0rd$ 1029 | z0fbcdef8,Passw0rd$ 1030 | z0fbcdef9,Passw0rd$ 1031 | z0fcbaef0,Passw0rd$ 1032 | z0fcbaef1,Passw0rd$ 1033 | z0fcbaef2,Passw0rd$ 1034 | z0fcbaef3,Passw0rd$ 1035 | z0fcbaef4,Passw0rd$ 1036 | z0fcbaef5,Passw0rd$ 1037 | z0fcbaef6,Passw0rd$ 1038 | z0fcbaef7,Passw0rd$ 1039 | z0fcbaef8,Passw0rd$ 1040 | z0fcbaef9,Passw0rd$ 1041 | z0fcbfea0,Passw0rd$ 1042 | z0fcbfea1,Passw0rd$ 1043 | z0fcbfea2,Passw0rd$ 1044 | z0fcbfea3,Passw0rd$ 1045 | z0fcbfea4,Passw0rd$ 1046 | z0fcbfea5,Passw0rd$ 1047 | z0fcbfea6,Passw0rd$ 1048 | z0fcbfea7,Passw0rd$ 1049 | z0fcbfea8,Passw0rd$ 1050 | z0fcbfea9,Passw0rd$ 1051 | z0hbcdef0,Passw0rd$ 1052 | z0hbcdef1,Passw0rd$ 1053 | z0hbcdef2,Passw0rd$ 1054 | z0hbcdef3,Passw0rd$ 1055 | z0hbcdef4,Passw0rd$ 1056 | z0hbcdef5,Passw0rd$ 1057 | z0hbcdef6,Passw0rd$ 1058 | z0hbcdef7,Passw0rd$ 1059 | z0hbcdef8,Passw0rd$ 1060 | z0hbcdef9,Passw0rd$ 1061 | z0hcbaef0,Passw0rd$ 1062 | z0hcbaef1,Passw0rd$ 1063 | z0hcbaef2,Passw0rd$ 1064 | z0hcbaef3,Passw0rd$ 1065 | z0hcbaef4,Passw0rd$ 1066 | z0hcbaef5,Passw0rd$ 1067 | z0hcbaef6,Passw0rd$ 1068 | z0hcbaef7,Passw0rd$ 1069 | z0hcbaef8,Passw0rd$ 1070 | z0hcbaef9,Passw0rd$ 1071 | z0hcbfea0,Passw0rd$ 1072 | z0hcbfea1,Passw0rd$ 1073 | z0hcbfea2,Passw0rd$ 1074 | z0hcbfea3,Passw0rd$ 1075 | z0hcbfea4,Passw0rd$ 1076 | z0hcbfea5,Passw0rd$ 1077 | z0hcbfea6,Passw0rd$ 1078 | z0hcbfea7,Passw0rd$ 1079 | z0hcbfea8,Passw0rd$ 1080 | z0hcbfea9,Passw0rd$ 1081 | z0ibcdef0,Passw0rd$ 1082 | z0ibcdef1,Passw0rd$ 1083 | z0ibcdef2,Passw0rd$ 1084 | z0ibcdef3,Passw0rd$ 1085 | z0ibcdef4,Passw0rd$ 1086 | z0ibcdef5,Passw0rd$ 1087 | z0ibcdef6,Passw0rd$ 1088 | z0ibcdef7,Passw0rd$ 1089 | z0ibcdef8,Passw0rd$ 1090 | z0ibcdef9,Passw0rd$ 1091 | z0icbaef0,Passw0rd$ 1092 | z0icbaef1,Passw0rd$ 1093 | z0icbaef2,Passw0rd$ 1094 | z0icbaef3,Passw0rd$ 1095 | z0icbaef4,Passw0rd$ 1096 | z0icbaef5,Passw0rd$ 1097 | z0icbaef6,Passw0rd$ 1098 | z0icbaef7,Passw0rd$ 1099 | z0icbaef8,Passw0rd$ 1100 | z0icbaef9,Passw0rd$ 1101 | z0icbfea0,Passw0rd$ 1102 | z0icbfea1,Passw0rd$ 1103 | z0icbfea2,Passw0rd$ 1104 | z0icbfea3,Passw0rd$ 1105 | z0icbfea4,Passw0rd$ 1106 | z0icbfea5,Passw0rd$ 1107 | z0icbfea6,Passw0rd$ 1108 | z0icbfea7,Passw0rd$ 1109 | z0icbfea8,Passw0rd$ 1110 | z0icbfea9,Passw0rd$ 1111 | z0jbcdef0,Passw0rd$ 1112 | z0jbcdef1,Passw0rd$ 1113 | z0jbcdef2,Passw0rd$ 1114 | z0jbcdef3,Passw0rd$ 1115 | z0jbcdef4,Passw0rd$ 1116 | z0jbcdef5,Passw0rd$ 1117 | z0jbcdef6,Passw0rd$ 1118 | z0jbcdef7,Passw0rd$ 1119 | z0jbcdef8,Passw0rd$ 1120 | z0jbcdef9,Passw0rd$ 1121 | z0jcbaef0,Passw0rd$ 1122 | z0jcbaef1,Passw0rd$ 1123 | z0jcbaef2,Passw0rd$ 1124 | z0jcbaef3,Passw0rd$ 1125 | z0jcbaef4,Passw0rd$ 1126 | z0jcbaef5,Passw0rd$ 1127 | z0jcbaef6,Passw0rd$ 1128 | z0jcbaef7,Passw0rd$ 1129 | z0jcbaef8,Passw0rd$ 1130 | z0jcbaef9,Passw0rd$ 1131 | z0jcbfea0,Passw0rd$ 1132 | z0jcbfea1,Passw0rd$ 1133 | z0jcbfea2,Passw0rd$ 1134 | z0jcbfea3,Passw0rd$ 1135 | z0jcbfea4,Passw0rd$ 1136 | z0jcbfea5,Passw0rd$ 1137 | z0jcbfea6,Passw0rd$ 1138 | z0jcbfea7,Passw0rd$ 1139 | z0jcbfea8,Passw0rd$ 1140 | z0jcbfea9,Passw0rd$ 1141 | z1abcdef0,Passw0rd$ 1142 | z1abcdef1,Passw0rd$ 1143 | z1abcdef2,Passw0rd$ 1144 | z1abcdef3,Passw0rd$ 1145 | z1abcdef4,Passw0rd$ 1146 | z1abcdef5,Passw0rd$ 1147 | z1abcdef6,Passw0rd$ 1148 | z1abcdef7,Passw0rd$ 1149 | z1abcdef8,Passw0rd$ 1150 | z1abcdef9,Passw0rd$ 1151 | z1bbcdef0,Passw0rd$ 1152 | z1bbcdef1,Passw0rd$ 1153 | z1bbcdef2,Passw0rd$ 1154 | z1bbcdef3,Passw0rd$ 1155 | z1bbcdef4,Passw0rd$ 1156 | z1bbcdef5,Passw0rd$ 1157 | z1bbcdef6,Passw0rd$ 1158 | z1bbcdef7,Passw0rd$ 1159 | z1bbcdef8,Passw0rd$ 1160 | z1bbcdef9,Passw0rd$ 1161 | z1cbcdef0,Passw0rd$ 1162 | z1cbcdef1,Passw0rd$ 1163 | z1cbcdef2,Passw0rd$ 1164 | z1cbcdef3,Passw0rd$ 1165 | z1cbcdef4,Passw0rd$ 1166 | z1cbcdef5,Passw0rd$ 1167 | z1cbcdef6,Passw0rd$ 1168 | z1cbcdef7,Passw0rd$ 1169 | z1cbcdef8,Passw0rd$ 1170 | z1cbcdef9,Passw0rd$ 1171 | z1dbcdef0,Passw0rd$ 1172 | z1dbcdef1,Passw0rd$ 1173 | z1dbcdef2,Passw0rd$ 1174 | z1dbcdef3,Passw0rd$ 1175 | z1dbcdef4,Passw0rd$ 1176 | z1dbcdef5,Passw0rd$ 1177 | z1dbcdef6,Passw0rd$ 1178 | z1dbcdef7,Passw0rd$ 1179 | z1dbcdef8,Passw0rd$ 1180 | z1dbcdef9,Passw0rd$ 1181 | z1dcbaef0,Passw0rd$ 1182 | z1dcbaef1,Passw0rd$ 1183 | z1dcbaef2,Passw0rd$ 1184 | z1dcbaef3,Passw0rd$ 1185 | z1dcbaef4,Passw0rd$ 1186 | z1dcbaef5,Passw0rd$ 1187 | z1dcbaef6,Passw0rd$ 1188 | z1dcbaef7,Passw0rd$ 1189 | z1dcbaef8,Passw0rd$ 1190 | z1dcbaef9,Passw0rd$ 1191 | z1dcbfea0,Passw0rd$ 1192 | z1dcbfea1,Passw0rd$ 1193 | z1dcbfea2,Passw0rd$ 1194 | z1dcbfea3,Passw0rd$ 1195 | z1dcbfea4,Passw0rd$ 1196 | z1dcbfea5,Passw0rd$ 1197 | z1dcbfea6,Passw0rd$ 1198 | z1dcbfea7,Passw0rd$ 1199 | z1dcbfea8,Passw0rd$ 1200 | z1dcbfea9,Passw0rd$ 1201 | z1ebcdef0,Passw0rd$ 1202 | z1ebcdef1,Passw0rd$ 1203 | z1ebcdef2,Passw0rd$ 1204 | z1ebcdef3,Passw0rd$ 1205 | z1ebcdef4,Passw0rd$ 1206 | z1ebcdef5,Passw0rd$ 1207 | z1ebcdef6,Passw0rd$ 1208 | z1ebcdef7,Passw0rd$ 1209 | z1ebcdef8,Passw0rd$ 1210 | z1ebcdef9,Passw0rd$ 1211 | z1fbcdef0,Passw0rd$ 1212 | z1fbcdef1,Passw0rd$ 1213 | z1fbcdef2,Passw0rd$ 1214 | z1fbcdef3,Passw0rd$ 1215 | z1fbcdef4,Passw0rd$ 1216 | z1fbcdef5,Passw0rd$ 1217 | z1fbcdef6,Passw0rd$ 1218 | z1fbcdef7,Passw0rd$ 1219 | z1fbcdef8,Passw0rd$ 1220 | z1fbcdef9,Passw0rd$ 1221 | z1fcbaef0,Passw0rd$ 1222 | z1fcbaef1,Passw0rd$ 1223 | z1fcbaef2,Passw0rd$ 1224 | z1fcbaef3,Passw0rd$ 1225 | z1fcbaef4,Passw0rd$ 1226 | z1fcbaef5,Passw0rd$ 1227 | z1fcbaef6,Passw0rd$ 1228 | z1fcbaef7,Passw0rd$ 1229 | z1fcbaef8,Passw0rd$ 1230 | z1fcbaef9,Passw0rd$ 1231 | z1fcbfea0,Passw0rd$ 1232 | z1fcbfea1,Passw0rd$ 1233 | z1fcbfea2,Passw0rd$ 1234 | z1fcbfea3,Passw0rd$ 1235 | z1fcbfea4,Passw0rd$ 1236 | z1fcbfea5,Passw0rd$ 1237 | z1fcbfea6,Passw0rd$ 1238 | z1fcbfea7,Passw0rd$ 1239 | z1fcbfea8,Passw0rd$ 1240 | z1fcbfea9,Passw0rd$ 1241 | z1hbcdef0,Passw0rd$ 1242 | z1hbcdef1,Passw0rd$ 1243 | z1hbcdef2,Passw0rd$ 1244 | z1hbcdef3,Passw0rd$ 1245 | z1hbcdef4,Passw0rd$ 1246 | z1hbcdef5,Passw0rd$ 1247 | z1hbcdef6,Passw0rd$ 1248 | z1hbcdef7,Passw0rd$ 1249 | z1hbcdef8,Passw0rd$ 1250 | z1hbcdef9,Passw0rd$ 1251 | z1hcbaef0,Passw0rd$ 1252 | z1hcbaef1,Passw0rd$ 1253 | z1hcbaef2,Passw0rd$ 1254 | z1hcbaef3,Passw0rd$ 1255 | z1hcbaef4,Passw0rd$ 1256 | z1hcbaef5,Passw0rd$ 1257 | z1hcbaef6,Passw0rd$ 1258 | z1hcbaef7,Passw0rd$ 1259 | z1hcbaef8,Passw0rd$ 1260 | z1hcbaef9,Passw0rd$ 1261 | z1hcbfea0,Passw0rd$ 1262 | z1hcbfea1,Passw0rd$ 1263 | z1hcbfea2,Passw0rd$ 1264 | z1hcbfea3,Passw0rd$ 1265 | z1hcbfea4,Passw0rd$ 1266 | z1hcbfea5,Passw0rd$ 1267 | z1hcbfea6,Passw0rd$ 1268 | z1hcbfea7,Passw0rd$ 1269 | z1hcbfea8,Passw0rd$ 1270 | z1hcbfea9,Passw0rd$ 1271 | z1ibcdef0,Passw0rd$ 1272 | z1ibcdef1,Passw0rd$ 1273 | z1ibcdef2,Passw0rd$ 1274 | z1ibcdef3,Passw0rd$ 1275 | z1ibcdef4,Passw0rd$ 1276 | z1ibcdef5,Passw0rd$ 1277 | z1ibcdef6,Passw0rd$ 1278 | z1ibcdef7,Passw0rd$ 1279 | z1ibcdef8,Passw0rd$ 1280 | z1ibcdef9,Passw0rd$ 1281 | z1icbaef0,Passw0rd$ 1282 | z1icbaef1,Passw0rd$ 1283 | z1icbaef2,Passw0rd$ 1284 | z1icbaef3,Passw0rd$ 1285 | z1icbaef4,Passw0rd$ 1286 | z1icbaef5,Passw0rd$ 1287 | z1icbaef6,Passw0rd$ 1288 | z1icbaef7,Passw0rd$ 1289 | z1icbaef8,Passw0rd$ 1290 | z1icbaef9,Passw0rd$ 1291 | z1icbfea0,Passw0rd$ 1292 | z1icbfea1,Passw0rd$ 1293 | z1icbfea2,Passw0rd$ 1294 | z1icbfea3,Passw0rd$ 1295 | z1icbfea4,Passw0rd$ 1296 | z1icbfea5,Passw0rd$ 1297 | z1icbfea6,Passw0rd$ 1298 | z1icbfea7,Passw0rd$ 1299 | z1icbfea8,Passw0rd$ 1300 | z1icbfea9,Passw0rd$ 1301 | z1jbcdef0,Passw0rd$ 1302 | z1jbcdef1,Passw0rd$ 1303 | z1jbcdef2,Passw0rd$ 1304 | z1jbcdef3,Passw0rd$ 1305 | z1jbcdef4,Passw0rd$ 1306 | z1jbcdef5,Passw0rd$ 1307 | z1jbcdef6,Passw0rd$ 1308 | z1jbcdef7,Passw0rd$ 1309 | z1jbcdef8,Passw0rd$ 1310 | z1jbcdef9,Passw0rd$ 1311 | z1jcbaef0,Passw0rd$ 1312 | z1jcbaef1,Passw0rd$ 1313 | z1jcbaef2,Passw0rd$ 1314 | z1jcbaef3,Passw0rd$ 1315 | z1jcbaef4,Passw0rd$ 1316 | z1jcbaef5,Passw0rd$ 1317 | z1jcbaef6,Passw0rd$ 1318 | z1jcbaef7,Passw0rd$ 1319 | z1jcbaef8,Passw0rd$ 1320 | z1jcbaef9,Passw0rd$ 1321 | z1jcbfea0,Passw0rd$ 1322 | z1jcbfea1,Passw0rd$ 1323 | z1jcbfea2,Passw0rd$ 1324 | z1jcbfea3,Passw0rd$ 1325 | z1jcbfea4,Passw0rd$ 1326 | z1jcbfea5,Passw0rd$ 1327 | z1jcbfea6,Passw0rd$ 1328 | z1jcbfea7,Passw0rd$ 1329 | z1jcbfea8,Passw0rd$ 1330 | z1jcbfea9,Passw0rd$ 1331 | z2abcdef0,Passw0rd$ 1332 | z2abcdef1,Passw0rd$ 1333 | z2abcdef2,Passw0rd$ 1334 | z2abcdef3,Passw0rd$ 1335 | z2abcdef4,Passw0rd$ 1336 | z2abcdef5,Passw0rd$ 1337 | z2abcdef6,Passw0rd$ 1338 | z2abcdef7,Passw0rd$ 1339 | z2abcdef8,Passw0rd$ 1340 | z2abcdef9,Passw0rd$ 1341 | z2bbcdef0,Passw0rd$ 1342 | z2bbcdef1,Passw0rd$ 1343 | z2bbcdef2,Passw0rd$ 1344 | z2bbcdef3,Passw0rd$ 1345 | z2bbcdef4,Passw0rd$ 1346 | z2bbcdef5,Passw0rd$ 1347 | z2bbcdef6,Passw0rd$ 1348 | z2bbcdef7,Passw0rd$ 1349 | z2bbcdef8,Passw0rd$ 1350 | z2bbcdef9,Passw0rd$ 1351 | z2cbcdef0,Passw0rd$ 1352 | z2cbcdef1,Passw0rd$ 1353 | z2cbcdef2,Passw0rd$ 1354 | z2cbcdef3,Passw0rd$ 1355 | z2cbcdef4,Passw0rd$ 1356 | z2cbcdef5,Passw0rd$ 1357 | z2cbcdef6,Passw0rd$ 1358 | z2cbcdef7,Passw0rd$ 1359 | z2cbcdef8,Passw0rd$ 1360 | z2cbcdef9,Passw0rd$ 1361 | z2dbcdef0,Passw0rd$ 1362 | z2dbcdef1,Passw0rd$ 1363 | z2dbcdef2,Passw0rd$ 1364 | z2dbcdef3,Passw0rd$ 1365 | z2dbcdef4,Passw0rd$ 1366 | z2dbcdef5,Passw0rd$ 1367 | z2dbcdef6,Passw0rd$ 1368 | z2dbcdef7,Passw0rd$ 1369 | z2dbcdef8,Passw0rd$ 1370 | z2dbcdef9,Passw0rd$ 1371 | z2dcbaef0,Passw0rd$ 1372 | z2dcbaef1,Passw0rd$ 1373 | z2dcbaef2,Passw0rd$ 1374 | z2dcbaef3,Passw0rd$ 1375 | z2dcbaef4,Passw0rd$ 1376 | z2dcbaef5,Passw0rd$ 1377 | z2dcbaef6,Passw0rd$ 1378 | z2dcbaef7,Passw0rd$ 1379 | z2dcbaef8,Passw0rd$ 1380 | z2dcbaef9,Passw0rd$ 1381 | z2dcbfea0,Passw0rd$ 1382 | z2dcbfea1,Passw0rd$ 1383 | z2dcbfea2,Passw0rd$ 1384 | z2dcbfea3,Passw0rd$ 1385 | z2dcbfea4,Passw0rd$ 1386 | z2dcbfea5,Passw0rd$ 1387 | z2dcbfea6,Passw0rd$ 1388 | z2dcbfea7,Passw0rd$ 1389 | z2dcbfea8,Passw0rd$ 1390 | z2dcbfea9,Passw0rd$ 1391 | z2ebcdef0,Passw0rd$ 1392 | z2ebcdef1,Passw0rd$ 1393 | z2ebcdef2,Passw0rd$ 1394 | z2ebcdef3,Passw0rd$ 1395 | z2ebcdef4,Passw0rd$ 1396 | z2ebcdef5,Passw0rd$ 1397 | z2ebcdef6,Passw0rd$ 1398 | z2ebcdef7,Passw0rd$ 1399 | z2ebcdef8,Passw0rd$ 1400 | z2ebcdef9,Passw0rd$ 1401 | z2fbcdef0,Passw0rd$ 1402 | z2fbcdef1,Passw0rd$ 1403 | z2fbcdef2,Passw0rd$ 1404 | z2fbcdef3,Passw0rd$ 1405 | z2fbcdef4,Passw0rd$ 1406 | z2fbcdef5,Passw0rd$ 1407 | z2fbcdef6,Passw0rd$ 1408 | z2fbcdef7,Passw0rd$ 1409 | z2fbcdef8,Passw0rd$ 1410 | z2fbcdef9,Passw0rd$ 1411 | z2fcbaef0,Passw0rd$ 1412 | z2fcbaef1,Passw0rd$ 1413 | z2fcbaef2,Passw0rd$ 1414 | z2fcbaef3,Passw0rd$ 1415 | z2fcbaef4,Passw0rd$ 1416 | z2fcbaef5,Passw0rd$ 1417 | z2fcbaef6,Passw0rd$ 1418 | z2fcbaef7,Passw0rd$ 1419 | z2fcbaef8,Passw0rd$ 1420 | z2fcbaef9,Passw0rd$ 1421 | z2fcbfea0,Passw0rd$ 1422 | z2fcbfea1,Passw0rd$ 1423 | z2fcbfea2,Passw0rd$ 1424 | z2fcbfea3,Passw0rd$ 1425 | z2fcbfea4,Passw0rd$ 1426 | z2fcbfea5,Passw0rd$ 1427 | z2fcbfea6,Passw0rd$ 1428 | z2fcbfea7,Passw0rd$ 1429 | z2fcbfea8,Passw0rd$ 1430 | z2fcbfea9,Passw0rd$ 1431 | z2hbcdef0,Passw0rd$ 1432 | z2hbcdef1,Passw0rd$ 1433 | z2hbcdef2,Passw0rd$ 1434 | z2hbcdef3,Passw0rd$ 1435 | z2hbcdef4,Passw0rd$ 1436 | z2hbcdef5,Passw0rd$ 1437 | z2hbcdef6,Passw0rd$ 1438 | z2hbcdef7,Passw0rd$ 1439 | z2hbcdef8,Passw0rd$ 1440 | z2hbcdef9,Passw0rd$ 1441 | z2hcbaef0,Passw0rd$ 1442 | z2hcbaef1,Passw0rd$ 1443 | z2hcbaef2,Passw0rd$ 1444 | z2hcbaef3,Passw0rd$ 1445 | z2hcbaef4,Passw0rd$ 1446 | z2hcbaef5,Passw0rd$ 1447 | z2hcbaef6,Passw0rd$ 1448 | z2hcbaef7,Passw0rd$ 1449 | z2hcbaef8,Passw0rd$ 1450 | z2hcbaef9,Passw0rd$ 1451 | z2hcbfea0,Passw0rd$ 1452 | z2hcbfea1,Passw0rd$ 1453 | z2hcbfea2,Passw0rd$ 1454 | z2hcbfea3,Passw0rd$ 1455 | z2hcbfea4,Passw0rd$ 1456 | z2hcbfea5,Passw0rd$ 1457 | z2hcbfea6,Passw0rd$ 1458 | z2hcbfea7,Passw0rd$ 1459 | z2hcbfea8,Passw0rd$ 1460 | z2hcbfea9,Passw0rd$ 1461 | z2ibcdef0,Passw0rd$ 1462 | z2ibcdef1,Passw0rd$ 1463 | z2ibcdef2,Passw0rd$ 1464 | z2ibcdef3,Passw0rd$ 1465 | z2ibcdef4,Passw0rd$ 1466 | z2ibcdef5,Passw0rd$ 1467 | z2ibcdef6,Passw0rd$ 1468 | z2ibcdef7,Passw0rd$ 1469 | z2ibcdef8,Passw0rd$ 1470 | z2ibcdef9,Passw0rd$ 1471 | z2icbaef0,Passw0rd$ 1472 | z2icbaef1,Passw0rd$ 1473 | z2icbaef2,Passw0rd$ 1474 | z2icbaef3,Passw0rd$ 1475 | z2icbaef4,Passw0rd$ 1476 | z2icbaef5,Passw0rd$ 1477 | z2icbaef6,Passw0rd$ 1478 | z2icbaef7,Passw0rd$ 1479 | z2icbaef8,Passw0rd$ 1480 | z2icbaef9,Passw0rd$ 1481 | z2icbfea0,Passw0rd$ 1482 | z2icbfea1,Passw0rd$ 1483 | z2icbfea2,Passw0rd$ 1484 | z2icbfea3,Passw0rd$ 1485 | z2icbfea4,Passw0rd$ 1486 | z2icbfea5,Passw0rd$ 1487 | z2icbfea6,Passw0rd$ 1488 | z2icbfea7,Passw0rd$ 1489 | z2icbfea8,Passw0rd$ 1490 | z2icbfea9,Passw0rd$ 1491 | z2jbcdef0,Passw0rd$ 1492 | z2jbcdef1,Passw0rd$ 1493 | z2jbcdef2,Passw0rd$ 1494 | z2jbcdef3,Passw0rd$ 1495 | z2jbcdef4,Passw0rd$ 1496 | z2jbcdef5,Passw0rd$ 1497 | z2jbcdef6,Passw0rd$ 1498 | z2jbcdef7,Passw0rd$ 1499 | z2jbcdef8,Passw0rd$ 1500 | z2jbcdef9,Passw0rd$ 1501 | z2jcbaef0,Passw0rd$ 1502 | z2jcbaef1,Passw0rd$ 1503 | z2jcbaef2,Passw0rd$ 1504 | z2jcbaef3,Passw0rd$ 1505 | z2jcbaef4,Passw0rd$ 1506 | z2jcbaef5,Passw0rd$ 1507 | z2jcbaef6,Passw0rd$ 1508 | z2jcbaef7,Passw0rd$ 1509 | z2jcbaef8,Passw0rd$ 1510 | z2jcbaef9,Passw0rd$ 1511 | z2jcbfea0,Passw0rd$ 1512 | z2jcbfea1,Passw0rd$ 1513 | z2jcbfea2,Passw0rd$ 1514 | z2jcbfea3,Passw0rd$ 1515 | z2jcbfea4,Passw0rd$ 1516 | z2jcbfea5,Passw0rd$ 1517 | z2jcbfea6,Passw0rd$ 1518 | z2jcbfea7,Passw0rd$ 1519 | z2jcbfea8,Passw0rd$ 1520 | z2jcbfea9,Passw0rd$ 1521 | z3abcdef0,Passw0rd$ 1522 | z3abcdef1,Passw0rd$ 1523 | z3abcdef2,Passw0rd$ 1524 | z3abcdef3,Passw0rd$ 1525 | z3abcdef4,Passw0rd$ 1526 | z3abcdef5,Passw0rd$ 1527 | z3abcdef6,Passw0rd$ 1528 | z3abcdef7,Passw0rd$ 1529 | z3abcdef8,Passw0rd$ 1530 | z3abcdef9,Passw0rd$ 1531 | z3bbcdef0,Passw0rd$ 1532 | z3bbcdef1,Passw0rd$ 1533 | z3bbcdef2,Passw0rd$ 1534 | z3bbcdef3,Passw0rd$ 1535 | z3bbcdef4,Passw0rd$ 1536 | z3bbcdef5,Passw0rd$ 1537 | z3bbcdef6,Passw0rd$ 1538 | z3bbcdef7,Passw0rd$ 1539 | z3bbcdef8,Passw0rd$ 1540 | z3bbcdef9,Passw0rd$ 1541 | z3cbcdef0,Passw0rd$ 1542 | z3cbcdef1,Passw0rd$ 1543 | z3cbcdef2,Passw0rd$ 1544 | z3cbcdef3,Passw0rd$ 1545 | z3cbcdef4,Passw0rd$ 1546 | z3cbcdef5,Passw0rd$ 1547 | z3cbcdef6,Passw0rd$ 1548 | z3cbcdef7,Passw0rd$ 1549 | z3cbcdef8,Passw0rd$ 1550 | z3cbcdef9,Passw0rd$ 1551 | z3dbcdef0,Passw0rd$ 1552 | z3dbcdef1,Passw0rd$ 1553 | z3dbcdef2,Passw0rd$ 1554 | z3dbcdef3,Passw0rd$ 1555 | z3dbcdef4,Passw0rd$ 1556 | z3dbcdef5,Passw0rd$ 1557 | z3dbcdef6,Passw0rd$ 1558 | z3dbcdef7,Passw0rd$ 1559 | z3dbcdef8,Passw0rd$ 1560 | z3dbcdef9,Passw0rd$ 1561 | z3dcbaef0,Passw0rd$ 1562 | z3dcbaef1,Passw0rd$ 1563 | z3dcbaef2,Passw0rd$ 1564 | z3dcbaef3,Passw0rd$ 1565 | z3dcbaef4,Passw0rd$ 1566 | z3dcbaef5,Passw0rd$ 1567 | z3dcbaef6,Passw0rd$ 1568 | z3dcbaef7,Passw0rd$ 1569 | z3dcbaef8,Passw0rd$ 1570 | z3dcbaef9,Passw0rd$ 1571 | z3dcbfea0,Passw0rd$ 1572 | z3dcbfea1,Passw0rd$ 1573 | z3dcbfea2,Passw0rd$ 1574 | z3dcbfea3,Passw0rd$ 1575 | z3dcbfea4,Passw0rd$ 1576 | z3dcbfea5,Passw0rd$ 1577 | z3dcbfea6,Passw0rd$ 1578 | z3dcbfea7,Passw0rd$ 1579 | z3dcbfea8,Passw0rd$ 1580 | z3dcbfea9,Passw0rd$ 1581 | z3ebcdef0,Passw0rd$ 1582 | z3ebcdef1,Passw0rd$ 1583 | z3ebcdef2,Passw0rd$ 1584 | z3ebcdef3,Passw0rd$ 1585 | z3ebcdef4,Passw0rd$ 1586 | z3ebcdef5,Passw0rd$ 1587 | z3ebcdef6,Passw0rd$ 1588 | z3ebcdef7,Passw0rd$ 1589 | z3ebcdef8,Passw0rd$ 1590 | z3ebcdef9,Passw0rd$ 1591 | z3fbcdef0,Passw0rd$ 1592 | z3fbcdef1,Passw0rd$ 1593 | z3fbcdef2,Passw0rd$ 1594 | z3fbcdef3,Passw0rd$ 1595 | z3fbcdef4,Passw0rd$ 1596 | z3fbcdef5,Passw0rd$ 1597 | z3fbcdef6,Passw0rd$ 1598 | z3fbcdef7,Passw0rd$ 1599 | z3fbcdef8,Passw0rd$ 1600 | z3fbcdef9,Passw0rd$ 1601 | z3fcbaef0,Passw0rd$ 1602 | z3fcbaef1,Passw0rd$ 1603 | z3fcbaef2,Passw0rd$ 1604 | z3fcbaef3,Passw0rd$ 1605 | z3fcbaef4,Passw0rd$ 1606 | z3fcbaef5,Passw0rd$ 1607 | z3fcbaef6,Passw0rd$ 1608 | z3fcbaef7,Passw0rd$ 1609 | z3fcbaef8,Passw0rd$ 1610 | z3fcbaef9,Passw0rd$ 1611 | z3fcbfea0,Passw0rd$ 1612 | z3fcbfea1,Passw0rd$ 1613 | z3fcbfea2,Passw0rd$ 1614 | z3fcbfea3,Passw0rd$ 1615 | z3fcbfea4,Passw0rd$ 1616 | z3fcbfea5,Passw0rd$ 1617 | z3fcbfea6,Passw0rd$ 1618 | z3fcbfea7,Passw0rd$ 1619 | z3fcbfea8,Passw0rd$ 1620 | z3fcbfea9,Passw0rd$ 1621 | z3hbcdef0,Passw0rd$ 1622 | z3hbcdef1,Passw0rd$ 1623 | z3hbcdef2,Passw0rd$ 1624 | z3hbcdef3,Passw0rd$ 1625 | z3hbcdef4,Passw0rd$ 1626 | z3hbcdef5,Passw0rd$ 1627 | z3hbcdef6,Passw0rd$ 1628 | z3hbcdef7,Passw0rd$ 1629 | z3hbcdef8,Passw0rd$ 1630 | z3hbcdef9,Passw0rd$ 1631 | z3hcbaef0,Passw0rd$ 1632 | z3hcbaef1,Passw0rd$ 1633 | z3hcbaef2,Passw0rd$ 1634 | z3hcbaef3,Passw0rd$ 1635 | z3hcbaef4,Passw0rd$ 1636 | z3hcbaef5,Passw0rd$ 1637 | z3hcbaef6,Passw0rd$ 1638 | z3hcbaef7,Passw0rd$ 1639 | z3hcbaef8,Passw0rd$ 1640 | z3hcbaef9,Passw0rd$ 1641 | z3hcbfea0,Passw0rd$ 1642 | z3hcbfea1,Passw0rd$ 1643 | z3hcbfea2,Passw0rd$ 1644 | z3hcbfea3,Passw0rd$ 1645 | z3hcbfea4,Passw0rd$ 1646 | z3hcbfea5,Passw0rd$ 1647 | z3hcbfea6,Passw0rd$ 1648 | z3hcbfea7,Passw0rd$ 1649 | z3hcbfea8,Passw0rd$ 1650 | z3hcbfea9,Passw0rd$ 1651 | z3ibcdef0,Passw0rd$ 1652 | z3ibcdef1,Passw0rd$ 1653 | z3ibcdef2,Passw0rd$ 1654 | z3ibcdef3,Passw0rd$ 1655 | z3ibcdef4,Passw0rd$ 1656 | z3ibcdef5,Passw0rd$ 1657 | z3ibcdef6,Passw0rd$ 1658 | z3ibcdef7,Passw0rd$ 1659 | z3ibcdef8,Passw0rd$ 1660 | z3ibcdef9,Passw0rd$ 1661 | z3icbaef0,Passw0rd$ 1662 | z3icbaef1,Passw0rd$ 1663 | z3icbaef2,Passw0rd$ 1664 | z3icbaef3,Passw0rd$ 1665 | z3icbaef4,Passw0rd$ 1666 | z3icbaef5,Passw0rd$ 1667 | z3icbaef6,Passw0rd$ 1668 | z3icbaef7,Passw0rd$ 1669 | z3icbaef8,Passw0rd$ 1670 | z3icbaef9,Passw0rd$ 1671 | z3icbfea0,Passw0rd$ 1672 | z3icbfea1,Passw0rd$ 1673 | z3icbfea2,Passw0rd$ 1674 | z3icbfea3,Passw0rd$ 1675 | z3icbfea4,Passw0rd$ 1676 | z3icbfea5,Passw0rd$ 1677 | z3icbfea6,Passw0rd$ 1678 | z3icbfea7,Passw0rd$ 1679 | z3icbfea8,Passw0rd$ 1680 | z3icbfea9,Passw0rd$ 1681 | z3jbcdef0,Passw0rd$ 1682 | z3jbcdef1,Passw0rd$ 1683 | z3jbcdef2,Passw0rd$ 1684 | z3jbcdef3,Passw0rd$ 1685 | z3jbcdef4,Passw0rd$ 1686 | z3jbcdef5,Passw0rd$ 1687 | z3jbcdef6,Passw0rd$ 1688 | z3jbcdef7,Passw0rd$ 1689 | z3jbcdef8,Passw0rd$ 1690 | z3jbcdef9,Passw0rd$ 1691 | z3jcbaef0,Passw0rd$ 1692 | z3jcbaef1,Passw0rd$ 1693 | z3jcbaef2,Passw0rd$ 1694 | z3jcbaef3,Passw0rd$ 1695 | z3jcbaef4,Passw0rd$ 1696 | z3jcbaef5,Passw0rd$ 1697 | z3jcbaef6,Passw0rd$ 1698 | z3jcbaef7,Passw0rd$ 1699 | z3jcbaef8,Passw0rd$ 1700 | z3jcbaef9,Passw0rd$ 1701 | z3jcbfea0,Passw0rd$ 1702 | z3jcbfea1,Passw0rd$ 1703 | z3jcbfea2,Passw0rd$ 1704 | z3jcbfea3,Passw0rd$ 1705 | z3jcbfea4,Passw0rd$ 1706 | z3jcbfea5,Passw0rd$ 1707 | z3jcbfea6,Passw0rd$ 1708 | z3jcbfea7,Passw0rd$ 1709 | z3jcbfea8,Passw0rd$ 1710 | z3jcbfea9,Passw0rd$ 1711 | zabcdef0,Passw0rd$ 1712 | zabcdef1,Passw0rd$ 1713 | zabcdef2,Passw0rd$ 1714 | zabcdef3,Passw0rd$ 1715 | zabcdef4,Passw0rd$ 1716 | zabcdef5,Passw0rd$ 1717 | zabcdef6,Passw0rd$ 1718 | zabcdef7,Passw0rd$ 1719 | zabcdef8,Passw0rd$ 1720 | zabcdef9,Passw0rd$ 1721 | zbbcdef0,Passw0rd$ 1722 | zbbcdef1,Passw0rd$ 1723 | zbbcdef2,Passw0rd$ 1724 | zbbcdef3,Passw0rd$ 1725 | zbbcdef4,Passw0rd$ 1726 | zbbcdef5,Passw0rd$ 1727 | zbbcdef6,Passw0rd$ 1728 | zbbcdef7,Passw0rd$ 1729 | zbbcdef8,Passw0rd$ 1730 | zbbcdef9,Passw0rd$ 1731 | zcbcdef0,Passw0rd$ 1732 | zcbcdef1,Passw0rd$ 1733 | zcbcdef2,Passw0rd$ 1734 | zcbcdef3,Passw0rd$ 1735 | zcbcdef4,Passw0rd$ 1736 | zcbcdef5,Passw0rd$ 1737 | zcbcdef6,Passw0rd$ 1738 | zcbcdef7,Passw0rd$ 1739 | zcbcdef8,Passw0rd$ 1740 | zcbcdef9,Passw0rd$ 1741 | zdbcdef0,Passw0rd$ 1742 | zdbcdef1,Passw0rd$ 1743 | zdbcdef2,Passw0rd$ 1744 | zdbcdef3,Passw0rd$ 1745 | zdbcdef4,Passw0rd$ 1746 | zdbcdef5,Passw0rd$ 1747 | zdbcdef6,Passw0rd$ 1748 | zdbcdef7,Passw0rd$ 1749 | zdbcdef8,Passw0rd$ 1750 | zdbcdef9,Passw0rd$ 1751 | zdcbaef0,Passw0rd$ 1752 | zdcbaef1,Passw0rd$ 1753 | zdcbaef2,Passw0rd$ 1754 | zdcbaef3,Passw0rd$ 1755 | zdcbaef4,Passw0rd$ 1756 | zdcbaef5,Passw0rd$ 1757 | zdcbaef6,Passw0rd$ 1758 | zdcbaef7,Passw0rd$ 1759 | zdcbaef8,Passw0rd$ 1760 | zdcbaef9,Passw0rd$ 1761 | zdcbfea0,Passw0rd$ 1762 | zdcbfea1,Passw0rd$ 1763 | zdcbfea2,Passw0rd$ 1764 | zdcbfea3,Passw0rd$ 1765 | zdcbfea4,Passw0rd$ 1766 | zdcbfea5,Passw0rd$ 1767 | zdcbfea6,Passw0rd$ 1768 | zdcbfea7,Passw0rd$ 1769 | zdcbfea8,Passw0rd$ 1770 | zdcbfea9,Passw0rd$ 1771 | zebcdef0,Passw0rd$ 1772 | zebcdef1,Passw0rd$ 1773 | zebcdef2,Passw0rd$ 1774 | zebcdef3,Passw0rd$ 1775 | zebcdef4,Passw0rd$ 1776 | zebcdef5,Passw0rd$ 1777 | zebcdef6,Passw0rd$ 1778 | zebcdef7,Passw0rd$ 1779 | zebcdef8,Passw0rd$ 1780 | zebcdef9,Passw0rd$ 1781 | zfbcdef0,Passw0rd$ 1782 | zfbcdef1,Passw0rd$ 1783 | zfbcdef2,Passw0rd$ 1784 | zfbcdef3,Passw0rd$ 1785 | zfbcdef4,Passw0rd$ 1786 | zfbcdef5,Passw0rd$ 1787 | zfbcdef6,Passw0rd$ 1788 | zfbcdef7,Passw0rd$ 1789 | zfbcdef8,Passw0rd$ 1790 | zfbcdef9,Passw0rd$ 1791 | zfcbaef0,Passw0rd$ 1792 | zfcbaef1,Passw0rd$ 1793 | zfcbaef2,Passw0rd$ 1794 | zfcbaef3,Passw0rd$ 1795 | zfcbaef4,Passw0rd$ 1796 | zfcbaef5,Passw0rd$ 1797 | zfcbaef6,Passw0rd$ 1798 | zfcbaef7,Passw0rd$ 1799 | zfcbaef8,Passw0rd$ 1800 | zfcbaef9,Passw0rd$ 1801 | zfcbfea0,Passw0rd$ 1802 | zfcbfea1,Passw0rd$ 1803 | zfcbfea2,Passw0rd$ 1804 | zfcbfea3,Passw0rd$ 1805 | zfcbfea4,Passw0rd$ 1806 | zfcbfea5,Passw0rd$ 1807 | zfcbfea6,Passw0rd$ 1808 | zfcbfea7,Passw0rd$ 1809 | zfcbfea8,Passw0rd$ 1810 | zfcbfea9,Passw0rd$ 1811 | zhbcdef0,Passw0rd$ 1812 | zhbcdef1,Passw0rd$ 1813 | zhbcdef2,Passw0rd$ 1814 | zhbcdef3,Passw0rd$ 1815 | zhbcdef4,Passw0rd$ 1816 | zhbcdef5,Passw0rd$ 1817 | zhbcdef6,Passw0rd$ 1818 | zhbcdef7,Passw0rd$ 1819 | zhbcdef8,Passw0rd$ 1820 | zhbcdef9,Passw0rd$ 1821 | zhcbaef0,Passw0rd$ 1822 | zhcbaef1,Passw0rd$ 1823 | zhcbaef2,Passw0rd$ 1824 | zhcbaef3,Passw0rd$ 1825 | zhcbaef4,Passw0rd$ 1826 | zhcbaef5,Passw0rd$ 1827 | zhcbaef6,Passw0rd$ 1828 | zhcbaef7,Passw0rd$ 1829 | zhcbaef8,Passw0rd$ 1830 | zhcbaef9,Passw0rd$ 1831 | zhcbfea0,Passw0rd$ 1832 | zhcbfea1,Passw0rd$ 1833 | zhcbfea2,Passw0rd$ 1834 | zhcbfea3,Passw0rd$ 1835 | zhcbfea4,Passw0rd$ 1836 | zhcbfea5,Passw0rd$ 1837 | zhcbfea6,Passw0rd$ 1838 | zhcbfea7,Passw0rd$ 1839 | zhcbfea8,Passw0rd$ 1840 | zhcbfea9,Passw0rd$ 1841 | zibcdef0,Passw0rd$ 1842 | zibcdef1,Passw0rd$ 1843 | zibcdef2,Passw0rd$ 1844 | zibcdef3,Passw0rd$ 1845 | zibcdef4,Passw0rd$ 1846 | zibcdef5,Passw0rd$ 1847 | zibcdef6,Passw0rd$ 1848 | zibcdef7,Passw0rd$ 1849 | zibcdef8,Passw0rd$ 1850 | zibcdef9,Passw0rd$ 1851 | zicbaef0,Passw0rd$ 1852 | zicbaef1,Passw0rd$ 1853 | zicbaef2,Passw0rd$ 1854 | zicbaef3,Passw0rd$ 1855 | zicbaef4,Passw0rd$ 1856 | zicbaef5,Passw0rd$ 1857 | zicbaef6,Passw0rd$ 1858 | zicbaef7,Passw0rd$ 1859 | zicbaef8,Passw0rd$ 1860 | zicbaef9,Passw0rd$ 1861 | zicbfea0,Passw0rd$ 1862 | zicbfea1,Passw0rd$ 1863 | zicbfea2,Passw0rd$ 1864 | zicbfea3,Passw0rd$ 1865 | zicbfea4,Passw0rd$ 1866 | zicbfea5,Passw0rd$ 1867 | zicbfea6,Passw0rd$ 1868 | zicbfea7,Passw0rd$ 1869 | zicbfea8,Passw0rd$ 1870 | zicbfea9,Passw0rd$ 1871 | zjbcdef0,Passw0rd$ 1872 | zjbcdef1,Passw0rd$ 1873 | zjbcdef2,Passw0rd$ 1874 | zjbcdef3,Passw0rd$ 1875 | zjbcdef4,Passw0rd$ 1876 | zjbcdef5,Passw0rd$ 1877 | zjbcdef6,Passw0rd$ 1878 | zjbcdef7,Passw0rd$ 1879 | zjbcdef8,Passw0rd$ 1880 | zjbcdef9,Passw0rd$ 1881 | zjcbaef0,Passw0rd$ 1882 | zjcbaef1,Passw0rd$ 1883 | zjcbaef2,Passw0rd$ 1884 | zjcbaef3,Passw0rd$ 1885 | zjcbaef4,Passw0rd$ 1886 | zjcbaef5,Passw0rd$ 1887 | zjcbaef6,Passw0rd$ 1888 | zjcbaef7,Passw0rd$ 1889 | zjcbaef8,Passw0rd$ 1890 | zjcbaef9,Passw0rd$ 1891 | zjcbfea0,Passw0rd$ 1892 | zjcbfea1,Passw0rd$ 1893 | zjcbfea2,Passw0rd$ 1894 | zjcbfea3,Passw0rd$ 1895 | zjcbfea4,Passw0rd$ 1896 | zjcbfea5,Passw0rd$ 1897 | zjcbfea6,Passw0rd$ 1898 | zjcbfea7,Passw0rd$ 1899 | zjcbfea8,Passw0rd$ 1900 | zjcbfea9,Passw0rd$ 1901 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "showflake-hapi-openshift", 3 | "version": "0.0.1", 4 | "description": "OpenShift/local Hapi Server for Snowflake Application", 5 | "keywords": [ 6 | "OpenShift", 7 | "Node.js", 8 | "snowflake", 9 | "openshift", 10 | "hapi" 11 | ], 12 | "author": { 13 | "name": "barton hammond" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/bartonhammond/snowflake-hapi-openshift" 18 | }, 19 | "engines": { 20 | "node": ">= 4.2.3", 21 | "npm": ">= 2.14.7" 22 | }, 23 | "scripts": { 24 | "test": "echo \"Error: no test specified\" && exit 1", 25 | "start": "cp ./README.md src/docs && node server.js", 26 | "debug": "node-debug server.js", 27 | "docs": "./node_modules/docker/docker -w -I -x node_modules -s yes -o ../snowflake-hapi-openshift-pages " 28 | }, 29 | "dependencies": { 30 | "boom": "^3.1.1", 31 | "good": "^6.4.0", 32 | "good-console": "^5.2.0", 33 | "handlebars": "^4.0.5", 34 | "hapi": "^11.1.3", 35 | "hapi-auth-jwt": "^4.0.0", 36 | "hapi-swagger": "^3.0.1", 37 | "hoek": "^3.0.4", 38 | "inert": "^3.2.0", 39 | "joi": "^7.1.0", 40 | "jsonwebtoken": "^5.5.0", 41 | "marked": "^0.3.5", 42 | "moment": "^2.10.6", 43 | "mongoose": "^4.3.4", 44 | "nodemailer": "^1.11.0", 45 | "path": "^0.12.7", 46 | "redis": "^2.4.2", 47 | "underscore": "^1.8.3", 48 | "vision": "^4.0.1" 49 | }, 50 | "devDependencies": { 51 | "docker": "^0.2.14" 52 | }, 53 | "bundleDependencies": [], 54 | "private": true, 55 | "main": "server.js" 56 | } 57 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * # snowflake 4 | * Snowflake ![snowflake](https://cloud.githubusercontent.com/assets/1282364/11599365/1a1c39d2-9a8c-11e5-8819-bc1e48b30525.png) 5 | */ 6 | 7 | /** 8 | * Hapi will be the NodeJS server. 9 | * I figure if WalMart, the largest retailer in the world, uses it, 10 | * it will work for me. 11 | * 12 | * From the command line, run ```npm start``` 13 | * 14 | * Hapi is configured in this import 15 | */ 16 | var HapiServer = require('./src/config/hapi'); 17 | 18 | /** 19 | * The mongodb will be used to store all the users 20 | */ 21 | require('./src/database/mongodb'); 22 | 23 | /** 24 | * When hapi starts up, some info is displayed 25 | */ 26 | HapiServer.start(function (err) { 27 | if (err) { 28 | console.log(err); 29 | return; 30 | }; 31 | console.log('Server is running: ' + HapiServer.info.uri); 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /src/assets/github-markdown.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: octicons-anchor; 3 | src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff'); 4 | } 5 | 6 | .markdown-body { 7 | -webkit-text-size-adjust: 100%; 8 | text-size-adjust: 100%; 9 | color: #333; 10 | font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 11 | font-size: 16px; 12 | line-height: 1.6; 13 | word-wrap: break-word; 14 | } 15 | 16 | .markdown-body a { 17 | background-color: transparent; 18 | } 19 | 20 | .markdown-body a:active, 21 | .markdown-body a:hover { 22 | outline: 0; 23 | } 24 | 25 | .markdown-body strong { 26 | font-weight: bold; 27 | } 28 | 29 | .markdown-body h1 { 30 | font-size: 2em; 31 | margin: 0.67em 0; 32 | } 33 | 34 | .markdown-body img { 35 | border: 0; 36 | } 37 | 38 | .markdown-body hr { 39 | box-sizing: content-box; 40 | height: 0; 41 | } 42 | 43 | .markdown-body pre { 44 | overflow: auto; 45 | } 46 | 47 | .markdown-body code, 48 | .markdown-body kbd, 49 | .markdown-body pre { 50 | font-family: monospace, monospace; 51 | font-size: 1em; 52 | } 53 | 54 | .markdown-body input { 55 | color: inherit; 56 | font: inherit; 57 | margin: 0; 58 | } 59 | 60 | .markdown-body html input[disabled] { 61 | cursor: default; 62 | } 63 | 64 | .markdown-body input { 65 | line-height: normal; 66 | } 67 | 68 | .markdown-body input[type="checkbox"] { 69 | box-sizing: border-box; 70 | padding: 0; 71 | } 72 | 73 | .markdown-body table { 74 | border-collapse: collapse; 75 | border-spacing: 0; 76 | } 77 | 78 | .markdown-body td, 79 | .markdown-body th { 80 | padding: 0; 81 | } 82 | 83 | .markdown-body * { 84 | box-sizing: border-box; 85 | } 86 | 87 | .markdown-body input { 88 | font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 89 | } 90 | 91 | .markdown-body a { 92 | color: #4078c0; 93 | text-decoration: none; 94 | } 95 | 96 | .markdown-body a:hover, 97 | .markdown-body a:active { 98 | text-decoration: underline; 99 | } 100 | 101 | .markdown-body hr { 102 | height: 0; 103 | margin: 15px 0; 104 | overflow: hidden; 105 | background: transparent; 106 | border: 0; 107 | border-bottom: 1px solid #ddd; 108 | } 109 | 110 | .markdown-body hr:before { 111 | display: table; 112 | content: ""; 113 | } 114 | 115 | .markdown-body hr:after { 116 | display: table; 117 | clear: both; 118 | content: ""; 119 | } 120 | 121 | .markdown-body h1, 122 | .markdown-body h2, 123 | .markdown-body h3, 124 | .markdown-body h4, 125 | .markdown-body h5, 126 | .markdown-body h6 { 127 | margin-top: 15px; 128 | margin-bottom: 15px; 129 | line-height: 1.1; 130 | } 131 | 132 | .markdown-body h1 { 133 | font-size: 30px; 134 | } 135 | 136 | .markdown-body h2 { 137 | font-size: 21px; 138 | } 139 | 140 | .markdown-body h3 { 141 | font-size: 16px; 142 | } 143 | 144 | .markdown-body h4 { 145 | font-size: 14px; 146 | } 147 | 148 | .markdown-body h5 { 149 | font-size: 12px; 150 | } 151 | 152 | .markdown-body h6 { 153 | font-size: 11px; 154 | } 155 | 156 | .markdown-body blockquote { 157 | margin: 0; 158 | } 159 | 160 | .markdown-body ul, 161 | .markdown-body ol { 162 | padding: 0; 163 | margin-top: 0; 164 | margin-bottom: 0; 165 | } 166 | 167 | .markdown-body ol ol, 168 | .markdown-body ul ol { 169 | list-style-type: lower-roman; 170 | } 171 | 172 | .markdown-body ul ul ol, 173 | .markdown-body ul ol ol, 174 | .markdown-body ol ul ol, 175 | .markdown-body ol ol ol { 176 | list-style-type: lower-alpha; 177 | } 178 | 179 | .markdown-body dd { 180 | margin-left: 0; 181 | } 182 | 183 | .markdown-body code { 184 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 185 | font-size: 12px; 186 | } 187 | 188 | .markdown-body pre { 189 | margin-top: 0; 190 | margin-bottom: 0; 191 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; 192 | } 193 | 194 | .markdown-body .select::-ms-expand { 195 | opacity: 0; 196 | } 197 | 198 | .markdown-body .octicon { 199 | font: normal normal normal 16px/1 octicons-anchor; 200 | display: inline-block; 201 | text-decoration: none; 202 | text-rendering: auto; 203 | -webkit-font-smoothing: antialiased; 204 | -moz-osx-font-smoothing: grayscale; 205 | -webkit-user-select: none; 206 | -moz-user-select: none; 207 | -ms-user-select: none; 208 | user-select: none; 209 | } 210 | 211 | .markdown-body .octicon-link:before { 212 | content: '\f05c'; 213 | } 214 | 215 | .markdown-body>*:first-child { 216 | margin-top: 0 !important; 217 | } 218 | 219 | .markdown-body>*:last-child { 220 | margin-bottom: 0 !important; 221 | } 222 | 223 | .markdown-body a:not([href]) { 224 | color: inherit; 225 | text-decoration: none; 226 | } 227 | 228 | .markdown-body .anchor { 229 | display: inline-block; 230 | padding-right: 2px; 231 | margin-left: -18px; 232 | } 233 | 234 | .markdown-body .anchor:focus { 235 | outline: none; 236 | } 237 | 238 | .markdown-body h1, 239 | .markdown-body h2, 240 | .markdown-body h3, 241 | .markdown-body h4, 242 | .markdown-body h5, 243 | .markdown-body h6 { 244 | margin-top: 1em; 245 | margin-bottom: 16px; 246 | font-weight: bold; 247 | line-height: 1.4; 248 | } 249 | 250 | .markdown-body h1 .octicon-link, 251 | .markdown-body h2 .octicon-link, 252 | .markdown-body h3 .octicon-link, 253 | .markdown-body h4 .octicon-link, 254 | .markdown-body h5 .octicon-link, 255 | .markdown-body h6 .octicon-link { 256 | color: #000; 257 | vertical-align: middle; 258 | visibility: hidden; 259 | } 260 | 261 | .markdown-body h1:hover .anchor, 262 | .markdown-body h2:hover .anchor, 263 | .markdown-body h3:hover .anchor, 264 | .markdown-body h4:hover .anchor, 265 | .markdown-body h5:hover .anchor, 266 | .markdown-body h6:hover .anchor { 267 | text-decoration: none; 268 | } 269 | 270 | .markdown-body h1:hover .anchor .octicon-link, 271 | .markdown-body h2:hover .anchor .octicon-link, 272 | .markdown-body h3:hover .anchor .octicon-link, 273 | .markdown-body h4:hover .anchor .octicon-link, 274 | .markdown-body h5:hover .anchor .octicon-link, 275 | .markdown-body h6:hover .anchor .octicon-link { 276 | visibility: visible; 277 | } 278 | 279 | .markdown-body h1 { 280 | padding-bottom: 0.3em; 281 | font-size: 2.25em; 282 | line-height: 1.2; 283 | border-bottom: 1px solid #eee; 284 | } 285 | 286 | .markdown-body h1 .anchor { 287 | line-height: 1; 288 | } 289 | 290 | .markdown-body h2 { 291 | padding-bottom: 0.3em; 292 | font-size: 1.75em; 293 | line-height: 1.225; 294 | border-bottom: 1px solid #eee; 295 | } 296 | 297 | .markdown-body h2 .anchor { 298 | line-height: 1; 299 | } 300 | 301 | .markdown-body h3 { 302 | font-size: 1.5em; 303 | line-height: 1.43; 304 | } 305 | 306 | .markdown-body h3 .anchor { 307 | line-height: 1.2; 308 | } 309 | 310 | .markdown-body h4 { 311 | font-size: 1.25em; 312 | } 313 | 314 | .markdown-body h4 .anchor { 315 | line-height: 1.2; 316 | } 317 | 318 | .markdown-body h5 { 319 | font-size: 1em; 320 | } 321 | 322 | .markdown-body h5 .anchor { 323 | line-height: 1.1; 324 | } 325 | 326 | .markdown-body h6 { 327 | font-size: 1em; 328 | color: #777; 329 | } 330 | 331 | .markdown-body h6 .anchor { 332 | line-height: 1.1; 333 | } 334 | 335 | .markdown-body p, 336 | .markdown-body blockquote, 337 | .markdown-body ul, 338 | .markdown-body ol, 339 | .markdown-body dl, 340 | .markdown-body table, 341 | .markdown-body pre { 342 | margin-top: 0; 343 | margin-bottom: 16px; 344 | } 345 | 346 | .markdown-body hr { 347 | height: 4px; 348 | padding: 0; 349 | margin: 16px 0; 350 | background-color: #e7e7e7; 351 | border: 0 none; 352 | } 353 | 354 | .markdown-body ul, 355 | .markdown-body ol { 356 | padding-left: 2em; 357 | } 358 | 359 | .markdown-body ul ul, 360 | .markdown-body ul ol, 361 | .markdown-body ol ol, 362 | .markdown-body ol ul { 363 | margin-top: 0; 364 | margin-bottom: 0; 365 | } 366 | 367 | .markdown-body li>p { 368 | margin-top: 16px; 369 | } 370 | 371 | .markdown-body dl { 372 | padding: 0; 373 | } 374 | 375 | .markdown-body dl dt { 376 | padding: 0; 377 | margin-top: 16px; 378 | font-size: 1em; 379 | font-style: italic; 380 | font-weight: bold; 381 | } 382 | 383 | .markdown-body dl dd { 384 | padding: 0 16px; 385 | margin-bottom: 16px; 386 | } 387 | 388 | .markdown-body blockquote { 389 | padding: 0 15px; 390 | color: #777; 391 | border-left: 4px solid #ddd; 392 | } 393 | 394 | .markdown-body blockquote>:first-child { 395 | margin-top: 0; 396 | } 397 | 398 | .markdown-body blockquote>:last-child { 399 | margin-bottom: 0; 400 | } 401 | 402 | .markdown-body table { 403 | display: block; 404 | width: 100%; 405 | overflow: auto; 406 | word-break: normal; 407 | word-break: keep-all; 408 | } 409 | 410 | .markdown-body table th { 411 | font-weight: bold; 412 | } 413 | 414 | .markdown-body table th, 415 | .markdown-body table td { 416 | padding: 6px 13px; 417 | border: 1px solid #ddd; 418 | } 419 | 420 | .markdown-body table tr { 421 | background-color: #fff; 422 | border-top: 1px solid #ccc; 423 | } 424 | 425 | .markdown-body table tr:nth-child(2n) { 426 | background-color: #f8f8f8; 427 | } 428 | 429 | .markdown-body img { 430 | max-width: 100%; 431 | box-sizing: content-box; 432 | background-color: #fff; 433 | } 434 | 435 | .markdown-body code { 436 | padding: 0; 437 | padding-top: 0.2em; 438 | padding-bottom: 0.2em; 439 | margin: 0; 440 | font-size: 85%; 441 | background-color: rgba(0,0,0,0.04); 442 | border-radius: 3px; 443 | } 444 | 445 | .markdown-body code:before, 446 | .markdown-body code:after { 447 | letter-spacing: -0.2em; 448 | content: "\00a0"; 449 | } 450 | 451 | .markdown-body pre>code { 452 | padding: 0; 453 | margin: 0; 454 | font-size: 100%; 455 | word-break: normal; 456 | white-space: pre; 457 | background: transparent; 458 | border: 0; 459 | } 460 | 461 | .markdown-body .highlight { 462 | margin-bottom: 16px; 463 | } 464 | 465 | .markdown-body .highlight pre, 466 | .markdown-body pre { 467 | padding: 16px; 468 | overflow: auto; 469 | font-size: 85%; 470 | line-height: 1.45; 471 | background-color: #f7f7f7; 472 | border-radius: 3px; 473 | } 474 | 475 | .markdown-body .highlight pre { 476 | margin-bottom: 0; 477 | word-break: normal; 478 | } 479 | 480 | .markdown-body pre { 481 | word-wrap: normal; 482 | } 483 | 484 | .markdown-body pre code { 485 | display: inline; 486 | max-width: initial; 487 | padding: 0; 488 | margin: 0; 489 | overflow: initial; 490 | line-height: inherit; 491 | word-wrap: normal; 492 | background-color: transparent; 493 | border: 0; 494 | } 495 | 496 | .markdown-body pre code:before, 497 | .markdown-body pre code:after { 498 | content: normal; 499 | } 500 | 501 | .markdown-body kbd { 502 | display: inline-block; 503 | padding: 3px 5px; 504 | font-size: 11px; 505 | line-height: 10px; 506 | color: #555; 507 | vertical-align: middle; 508 | background-color: #fcfcfc; 509 | border: solid 1px #ccc; 510 | border-bottom-color: #bbb; 511 | border-radius: 3px; 512 | box-shadow: inset 0 -1px 0 #bbb; 513 | } 514 | 515 | .markdown-body .pl-c { 516 | color: #969896; 517 | } 518 | 519 | .markdown-body .pl-c1, 520 | .markdown-body .pl-s .pl-v { 521 | color: #0086b3; 522 | } 523 | 524 | .markdown-body .pl-e, 525 | .markdown-body .pl-en { 526 | color: #795da3; 527 | } 528 | 529 | .markdown-body .pl-s .pl-s1, 530 | .markdown-body .pl-smi { 531 | color: #333; 532 | } 533 | 534 | .markdown-body .pl-ent { 535 | color: #63a35c; 536 | } 537 | 538 | .markdown-body .pl-k { 539 | color: #a71d5d; 540 | } 541 | 542 | .markdown-body .pl-pds, 543 | .markdown-body .pl-s, 544 | .markdown-body .pl-s .pl-pse .pl-s1, 545 | .markdown-body .pl-sr, 546 | .markdown-body .pl-sr .pl-cce, 547 | .markdown-body .pl-sr .pl-sra, 548 | .markdown-body .pl-sr .pl-sre { 549 | color: #183691; 550 | } 551 | 552 | .markdown-body .pl-v { 553 | color: #ed6a43; 554 | } 555 | 556 | .markdown-body .pl-id { 557 | color: #b52a1d; 558 | } 559 | 560 | .markdown-body .pl-ii { 561 | background-color: #b52a1d; 562 | color: #f8f8f8; 563 | } 564 | 565 | .markdown-body .pl-sr .pl-cce { 566 | color: #63a35c; 567 | font-weight: bold; 568 | } 569 | 570 | .markdown-body .pl-ml { 571 | color: #693a17; 572 | } 573 | 574 | .markdown-body .pl-mh, 575 | .markdown-body .pl-mh .pl-en, 576 | .markdown-body .pl-ms { 577 | color: #1d3e81; 578 | font-weight: bold; 579 | } 580 | 581 | .markdown-body .pl-mq { 582 | color: #008080; 583 | } 584 | 585 | .markdown-body .pl-mi { 586 | color: #333; 587 | font-style: italic; 588 | } 589 | 590 | .markdown-body .pl-mb { 591 | color: #333; 592 | font-weight: bold; 593 | } 594 | 595 | .markdown-body .pl-md { 596 | background-color: #ffecec; 597 | color: #bd2c00; 598 | } 599 | 600 | .markdown-body .pl-mi1 { 601 | background-color: #eaffea; 602 | color: #55a532; 603 | } 604 | 605 | .markdown-body .pl-mdr { 606 | color: #795da3; 607 | font-weight: bold; 608 | } 609 | 610 | .markdown-body .pl-mo { 611 | color: #1d3e81; 612 | } 613 | 614 | .markdown-body kbd { 615 | display: inline-block; 616 | padding: 3px 5px; 617 | font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; 618 | line-height: 10px; 619 | color: #555; 620 | vertical-align: middle; 621 | background-color: #fcfcfc; 622 | border: solid 1px #ccc; 623 | border-bottom-color: #bbb; 624 | border-radius: 3px; 625 | box-shadow: inset 0 -1px 0 #bbb; 626 | } 627 | 628 | .markdown-body:before { 629 | display: table; 630 | content: ""; 631 | } 632 | 633 | .markdown-body:after { 634 | display: table; 635 | clear: both; 636 | content: ""; 637 | } 638 | 639 | .markdown-body .task-list-item { 640 | list-style-type: none; 641 | } 642 | 643 | .markdown-body .task-list-item+.task-list-item { 644 | margin-top: 3px; 645 | } 646 | 647 | .markdown-body .task-list-item input { 648 | margin: 0 0.35em 0.25em -1.6em; 649 | vertical-align: middle; 650 | } 651 | 652 | .markdown-body :checked+.radio-label { 653 | z-index: 1; 654 | position: relative; 655 | border-color: #4078c0; 656 | } -------------------------------------------------------------------------------- /src/assets/resetPassword.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # resetPassword.js 3 | * 4 | * This is a client side implementation that supports the reset 5 | * password user case. When the user presses the "reset" button a 6 | * AJAX call is sent to the server. 7 | * 8 | */ 9 | 10 | var internals = {}; 11 | 12 | /** 13 | * ## executeAjax 14 | * 15 | * Post the data to the url 16 | * 17 | */ 18 | internals.executeAJAX = function (url, data, callback) { 19 | 20 | internals.clearErrors(); 21 | 22 | var request = new XMLHttpRequest(); 23 | 24 | request.open('POST', url); 25 | 26 | request.timedOut = false; 27 | 28 | var requestTimer = setTimeout(function () { 29 | 30 | // Request timed out, Retry or inform user. 31 | request.timedOut = true; 32 | request.abort(); 33 | }, 3000); 34 | 35 | request.onreadystatechange = function () { 36 | if (request.readyState === 4 && callback) { 37 | return callback(request); 38 | } 39 | }; 40 | 41 | request.setRequestHeader('Content-Type', 'application/json'); 42 | 43 | request.send(JSON.stringify(data)); 44 | 45 | return true; 46 | }; 47 | 48 | /** 49 | * ## errorMessage 50 | * 51 | * display any error message 52 | * 53 | */ 54 | internals.errorMessage = function (message) { 55 | 56 | // handle error message 57 | 58 | var newP = document.createElement('p'); 59 | newP.innerHTML = message; 60 | 61 | var att = document.createAttribute('class'); 62 | att.value = 'errorMessage'; 63 | newP.setAttributeNode(att); 64 | 65 | var form = document.getElementsByTagName('form')[0]; 66 | 67 | var parentDiv = form.parentNode; 68 | 69 | parentDiv.insertBefore(newP, form); 70 | 71 | return false; 72 | }; 73 | /** 74 | * ## clearErrors 75 | * 76 | * Remove any errors 77 | * 78 | */ 79 | internals.clearErrors = function () { 80 | 81 | var errorMessages = document.getElementsByTagName('p'); 82 | 83 | if (errorMessages.length > 0) { 84 | console.log('Has Errors'); 85 | errorMessages[0].parentNode.removeChild(errorMessages[0]); 86 | } 87 | }; 88 | /** 89 | * ## successMessage 90 | * 91 | * Display the success message from the server 92 | * 93 | */ 94 | internals.successMessage = function (request) { 95 | 96 | 97 | // Success clear and display authenticated users links. 98 | 99 | 100 | var form = document.getElementsByTagName('form')[0]; 101 | var parent1 = form.parentNode; 102 | 103 | 104 | parent1.innerHTML = 'Your password has been reset'; 105 | 106 | return; 107 | }; 108 | 109 | /** 110 | * ## onreadystatechange 111 | * 112 | * List for click event on button, send data to server to reset password 113 | * 114 | */ 115 | document.onreadystatechange = function () { 116 | 117 | if (document.readyState === 'complete') { 118 | 119 | // Add click event handler 120 | document.getElementById('btnReset').addEventListener('click', function (event) { 121 | event.preventDefault(); 122 | 123 | // Get submitted form data 124 | var password = document.getElementsByName('password')[0].value; 125 | var token = document.getElementsByName('token')[0].value; 126 | 127 | var requestData = { password: password, token: token }; 128 | 129 | internals.executeAJAX('/account/resetPassword', requestData, function (request) { 130 | 131 | if (request.status === 200) { 132 | 133 | internals.successMessage(request); 134 | 135 | } else if (request.status !== 0) { 136 | 137 | // Boom error message received from server. 138 | var responseJson = JSON.parse(request.response); 139 | internals.errorMessage(responseJson.message); 140 | 141 | } else { 142 | 143 | // Oh no! request aborted 144 | if (request.timedOut === true) { 145 | internals.errorMessage('Your request timed out. Most likely you have a slow internet connection.'); 146 | } else { 147 | internals.errorMessage('[Error] Failed to load resource: Could not connect to the server.'); 148 | } 149 | } 150 | }); 151 | }); 152 | } 153 | }; 154 | -------------------------------------------------------------------------------- /src/auth/jwt-strategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # ErrorAlert.js 3 | * 4 | * This class uses a component which displays the appropriate alert 5 | * depending on the platform 6 | * 7 | * The main purpose here is to determine if there is an error and then 8 | * plucking off the message depending on the shape of the error object. 9 | */ 10 | 'use strict'; 11 | /** 12 | * ## Imports 13 | * 14 | */ 15 | var Config = require('../config'), 16 | internals = {}, 17 | //the authentication package 18 | Jwt = require('jsonwebtoken'), 19 | //redis for blacklisting tokens 20 | redisClient = require('../database/redis'), 21 | //mongoose user object 22 | User = require('../database/models/User.js'); 23 | 24 | // private key for signing 25 | internals.privateKey = Config.crypto.privateKey; 26 | 27 | /** 28 | * 29 | * ## validate 30 | * 31 | * When a route is configured w/ 'auth', this validate function is 32 | * invoked 33 | * 34 | * If the token wasn't invalidated w/ logout, then validate 35 | * its for a user 36 | * 37 | * When a user logs out, the token they were using is saved to Redis 38 | * and checked here to prevent re-use 39 | * 40 | */ 41 | internals.validate = function (request, decodedToken, callback) { 42 | 43 | var credentials = {}; 44 | 45 | //credentials have 'Bearer dfadfsdf' 46 | var headers = request.headers.authorization.split(' '); 47 | 48 | if (headers.length === 2) { 49 | //does redis have the token 50 | redisClient.get(headers[1], function (err, reply) { 51 | 52 | if (err) { 53 | return callback(err, false, credentials); 54 | } 55 | 56 | //oops - it's been blacklisted - sorry 57 | if (reply) { 58 | return callback({message: 'invalid auth token'}, false, credentials); 59 | } 60 | // ok - valid token, do we have a user? 61 | // note we're only using 'id' - that's because 62 | // the user can change their email and username 63 | User.findById(decodedToken.id, function (err, user) { 64 | 65 | if (err) { 66 | return callback(err, false, credentials); 67 | } else { 68 | credentials = user; 69 | 70 | return callback(err, true, credentials); 71 | } 72 | }); 73 | }); 74 | } 75 | 76 | 77 | 78 | }; 79 | 80 | // create token 81 | internals.createToken = function (obj) { 82 | return Jwt.sign(obj, internals.privateKey); 83 | }; 84 | 85 | // set jwt auth strategy 86 | internals.setJwtStrategy = function (server) { 87 | server.auth.strategy('token', 'jwt', { 88 | key: internals.privateKey, 89 | validateFunc: internals.validate 90 | }); 91 | }; 92 | 93 | module.exports = { 94 | setStrategy: internals.setJwtStrategy, 95 | createToken: internals.createToken 96 | }; 97 | -------------------------------------------------------------------------------- /src/config.sample.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongodb: { 3 | ip: '127.0.0.1', 4 | port: '27017', 5 | app: 'nodejs' 6 | }, 7 | redis: { 8 | host: '127.0.0.1', 9 | port: 6379 10 | }, 11 | crypto: { 12 | privateKey: 13 | '37LvDSasdfasfsaf3a3IEIA;3r3oi3joijpjfa3a3m4XvjYOh9Yaa.p3id#IEYDNeaken', 14 | tokenExpiry: 1 * 30 * 1000 * 60 //1 hour 15 | }, 16 | email: { 17 | test: true, 18 | username: "someone@gmail.com", 19 | password: "somepassword", 20 | accountName: "Snowflake" 21 | }, 22 | validation: { 23 | username: /^[a-zA-Z0-9]{6,12}$/, 24 | password: /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,12}$/ 25 | }, 26 | hapi: { 27 | port: 5000, 28 | ip: '192.168.1.66' 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/config/hapi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # Hapi.js 3 | * 4 | * This is a configuration for Hapi 5 | * 6 | * Note that Hapi is configuration over coding 7 | * 8 | * There's no coding here!! 9 | */ 10 | 'use strict'; 11 | 12 | /** 13 | * ## Imports 14 | * 15 | */ 16 | var 17 | Config = require('../config'), 18 | //Hapi itself 19 | Hapi = require('hapi'), 20 | // the authentication strategy 21 | JwtAuth = require('../auth/jwt-strategy'), 22 | // kind of like underscore, but specific to Hapi 23 | Hoek = require('hoek'), 24 | // some additional services 25 | Plugins = require('./plugins'), 26 | // the routes we'll support 27 | Routes = require('./routes'), 28 | // the view, mainly for reset password 29 | Views = require('./views'); 30 | 31 | var internals = {}; 32 | 33 | //The real Hapi server! 34 | internals.server = new Hapi.Server(); 35 | 36 | //Setup the connection for the environment 37 | internals.server.connection({ 38 | port: process.env.OPENSHIFT_NODEJS_PORT || Config.hapi.port, 39 | address: process.env.OPENSHIFT_NODEJS_IP || Config.hapi.ip 40 | }); 41 | 42 | // register plugins 43 | internals.server.register(Plugins.get(), (err) => { 44 | Hoek.assert(!err,err); 45 | }); 46 | // configure jwt strategy 47 | JwtAuth.setStrategy(internals.server); 48 | 49 | //setup views for resetpassword 50 | Views.init(internals.server); 51 | 52 | // set routes 53 | Routes.init(internals.server); 54 | 55 | module.exports = internals.server; 56 | -------------------------------------------------------------------------------- /src/config/methods.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartonhammond/snowflake-hapi-openshift/be9cc7f2052393bbd1b2efeccaacd80bae887bc0/src/config/methods.js -------------------------------------------------------------------------------- /src/config/plugins.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # plugins.js 3 | * 4 | * Plugins are like middleware, they get used 'automagically' 5 | * 6 | */ 7 | 'use strict'; 8 | 9 | 10 | var internals = {}; 11 | 12 | /** 13 | * ## plugins 14 | * 15 | * when a route is config'd with auth, the hapi-auth-jwt will be invoked 16 | * 17 | * the good module prints out messages 18 | */ 19 | internals.plugins = function () { 20 | return [ 21 | { 22 | register: require('hapi-auth-jwt'), 23 | options: {} 24 | }, 25 | { 26 | 27 | register: require('good'), 28 | options: { 29 | opsInterval: 1000, 30 | reporters: [{ 31 | reporter: require('good-console'), 32 | events: { log: '*', response: '*', request: '*' } 33 | }] 34 | } 35 | } 36 | ]; 37 | }; 38 | 39 | module.exports.get = internals.plugins; 40 | -------------------------------------------------------------------------------- /src/config/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # routes.js 3 | * 4 | * All the routes available are defined here 5 | * The endpoints descripe the method (POST/GET...) 6 | * and the url ('account/login') 7 | * and the handler 8 | * 9 | * 10 | */ 11 | 'use strict'; 12 | /** 13 | * ## All the routes are joined 14 | * 15 | */ 16 | // Accounts 17 | var AccountRoutes = require('../routes/account/endpoints'), 18 | //General like env & status 19 | GeneralRoutes = require('../routes/general/endpoints'), 20 | //Restricted route to prove authentication & authorization 21 | RestrictedRoutes = require('../routes/restricted/endpoints'); 22 | 23 | var internals = {}; 24 | 25 | //Concatentate the routes into one array 26 | internals.routes = [].concat(AccountRoutes.endpoints, 27 | GeneralRoutes.endpoints, 28 | RestrictedRoutes.endpoints); 29 | 30 | //set the routes for the server 31 | internals.init = function (server) { 32 | server.route(internals.routes); 33 | }; 34 | 35 | module.exports = internals; 36 | -------------------------------------------------------------------------------- /src/config/views.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # views.js 3 | * 4 | * Snowflake has only one view, reset password 5 | * 6 | * Using the module 'vision, establish the renderering engine, 7 | * in this case, handlebars, and where the views are located 8 | * 9 | * Since this reset password view needs a javascript file, 10 | * the location of this 'asset' is set. 11 | * 12 | */ 13 | 'use strict'; 14 | 15 | /** 16 | * ## Imports 17 | * 18 | */ 19 | // Hoek is similar to underscore 20 | var Handlebars = require('handlebars'), 21 | HapiSwagger = require('hapi-swagger'), 22 | Hoek = require('hoek'), 23 | internals = {}, 24 | Inert = require('inert'), 25 | Marked = require('marked'), 26 | Pack = require('../../package'), 27 | Path = require('path'), 28 | Vision = require('vision'); 29 | 30 | /** 31 | * ### markdown view 32 | * 33 | * Use the GitHub Markdown css 34 | */ 35 | function MarkdownView() { 36 | 37 | this.compile = function (template) { 38 | return function (context) { 39 | var html = Marked(template, context); 40 | return ` 41 | 50 |
51 | ${html} 52 |
`; 53 | }; 54 | }; 55 | } 56 | 57 | /** 58 | * ## init 59 | * 60 | */ 61 | internals.init = function (server) { 62 | 63 | /** 64 | * ### vision 65 | * 66 | * this establishes where the html is located 67 | * and the engine to parse it 68 | */ 69 | server.register(Vision, (err) => { 70 | 71 | Hoek.assert(!err,err); 72 | 73 | server.views({ 74 | engines: { 75 | 'html': Handlebars, 76 | 'md': { 77 | module: new MarkdownView(), 78 | contentType: 'text/html' 79 | } 80 | }, 81 | relativeTo: __dirname, 82 | path: [Path.join(__dirname, '../views'),Path.join(__dirname, '../docs')] 83 | }); 84 | 85 | }); 86 | /** 87 | * ### inert 88 | * 89 | * this says that any request for /assest/* will 90 | * be served from the ../assests dir 91 | * 92 | * The resetpassword.js is located in ../assests 93 | * 94 | */ 95 | server.register(Inert, (err) => { 96 | 97 | //Confirm no err 98 | Hoek.assert(!err,err); 99 | 100 | //Load files located in ../assets 101 | server.route({ 102 | method: 'GET', 103 | path: '/assets/{param*}', 104 | handler: { 105 | directory: { 106 | path: Path.join(__dirname, '../assets') 107 | } 108 | } 109 | }); 110 | }); 111 | 112 | /** 113 | * ### swagger 114 | * 115 | * Swagger documents the api 116 | * 117 | * the /documentation endpoint displays the api docs 118 | * 119 | */ 120 | const swaggerOptions = { 121 | info: { 122 | 'title': 'Snowflake - API Documentation', 123 | 'version': Pack.version 124 | } 125 | }; 126 | server.register({ 127 | register: HapiSwagger, 128 | options: swaggerOptions 129 | }, (err) => { 130 | Hoek.assert(!err,err); 131 | 132 | }); 133 | 134 | }; 135 | 136 | module.exports = internals; 137 | -------------------------------------------------------------------------------- /src/database/models/User.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # User.js 3 | * 4 | * The user document for Mongoose 5 | * 6 | */ 7 | 'use strict'; 8 | /** 9 | * ## Imports 10 | * 11 | */ 12 | //Mongoose - the ORM 13 | var Mongoose = require('mongoose'), 14 | //The document structure definition 15 | Schema = Mongoose.Schema; 16 | 17 | //Same fields as Parse.com 18 | var UserSchema = new Schema({ 19 | username: { 20 | type: String, 21 | unique: true, 22 | required: true 23 | }, 24 | email: { 25 | type: String, 26 | unique: true, 27 | required: true 28 | }, 29 | password:{ 30 | type: String, 31 | required: true 32 | }, 33 | emailVerified: { 34 | type: Boolean 35 | } 36 | }); 37 | //Make a compound index of username/email 38 | UserSchema.index({ username: 1, email: 1 }, { unique: true }); 39 | /** 40 | * ## findUserByIdAndUserName 41 | * 42 | * @param id - user _id from Mongodb 43 | * @param username - username field from Mongodb 44 | * @param callback - resolve the action 45 | * 46 | */ 47 | UserSchema.statics.findUserByIdAndUserName = function(id, username, callback) { 48 | this.findOne({ 49 | username: username, 50 | _id: id 51 | }, callback); 52 | }; 53 | /** 54 | * ## findUserByEmail 55 | * 56 | * @param email - the user document email field from Mongodb 57 | * @param callback - resolve the action 58 | * 59 | */ 60 | UserSchema.statics.findUserByEmail = function(email, callback) { 61 | this.findOne({ 62 | email: email 63 | }, callback); 64 | }; 65 | 66 | /** 67 | * ## Mongoose model for User 68 | * 69 | * @param UserSchema - the document structure definition 70 | * 71 | */ 72 | var user = Mongoose.model('User', UserSchema); 73 | 74 | module.exports = user; 75 | -------------------------------------------------------------------------------- /src/database/mongodb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # mongodb.js 3 | * 4 | * All the user information will be documents in MongoDB 5 | * 6 | * This class sets up the connection depending on the environment 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | /** 12 | * ## Imports 13 | * 14 | */ 15 | //use mongoose as the ORM 16 | var Mongoose = require('mongoose'), 17 | Config = require('../config'); 18 | 19 | 20 | /** 21 | * ## Default the connection string to the development envionment 22 | * 23 | */ 24 | var connection_string = Config.mongodb.ip 25 | + ':' 26 | + Config.mongodb.port 27 | + '/' 28 | + Config.mongodb.app; 29 | 30 | /** 31 | * ## Set the connection string for the OpenShift environment 32 | * 33 | */ 34 | if(process.env.OPENSHIFT_MONGODB_DB_HOST){ 35 | connection_string = 36 | process.env.OPENSHIFT_MONGODB_DB_USERNAME + ":" + 37 | process.env.OPENSHIFT_MONGODB_DB_PASSWORD + "@" + 38 | process.env.OPENSHIFT_MONGODB_DB_HOST + ':' + 39 | process.env.OPENSHIFT_MONGODB_DB_PORT + '/' + 40 | process.env.OPENSHIFT_APP_NAME; 41 | } 42 | Mongoose.connect(connection_string); 43 | -------------------------------------------------------------------------------- /src/database/redis.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # redis.js 3 | * 4 | * This is the configuration for Redis 5 | * 6 | */ 7 | 'use strict'; 8 | /** 9 | * ## Imports 10 | * 11 | */ 12 | var Redis = require('redis'), 13 | Config = require('../config'), 14 | redisClient = {}; 15 | /** 16 | * ## The connect string for the dev environment 17 | * 18 | */ 19 | var connection_string = Config.redis; 20 | /** 21 | * 22 | * ## The connect string for the OpenShift env 23 | * 24 | */ 25 | if(process.env.OPENSHIFT_REDIS_DB_HOST){ 26 | //The redis env variables on openshift 27 | connection_string.host = process.env.OPENSHIFT_REDIS_DB_HOST; 28 | connection_string.port = process.env.OPENSHIFT_REDIS_DB_PORT; 29 | 30 | //connect to Redis 31 | redisClient = Redis.createClient(connection_string); 32 | 33 | //have to authenticate 34 | redisClient.auth( process.env.OPENSHIFT_REDIS_DB_PASSWORD); 35 | } else { 36 | 37 | //running locally - make sure you've started redis server 38 | redisClient = Redis.createClient(connection_string); 39 | } 40 | 41 | module.exports = redisClient; 42 | 43 | -------------------------------------------------------------------------------- /src/docs/README.md: -------------------------------------------------------------------------------- 1 | Snowflake Server ![Snowflake Server](https://cloud.githubusercontent.com/assets/1282364/12075658/12d1cfee-b14c-11e5-9aa5-dc7fd1f0c795.png) 2 | ================================== 3 | ### This is a Hapi server, backed by MongoDb & Redis, running *freely* on Openshift for the [React-Native mobile app Snowflake](https://github.com/bartonhammond/snowflake). 4 | 5 | ####You can consider this a "starter server" or a "boilerplate" or maybe just an "example" of how these technologies work together. In any case, I hope there's something here you can learn and apply to your project! 6 | 7 | | Services | Javascript | noSQL | 8 | | :------------:|:---------------:| :-----:| 9 | | OpenShift | hapi | mongodb | 10 | | jmeter | swagger | redis| 11 | | blazeMeter| nodeMailer | mongoose | 12 | 13 | 14 | * **OpenShift** [https://www.openshift.com/](https://www.openshift.com/) - OpenShift Online is Red Hat's next-generation application hosting platform that makes it easy to run your web applications in the cloud for free. 15 | * **Hapi** [http://hapijs.com/](http://hapijs.com/) - A rich framework for building applications and services 16 | * **MongoDb** [https://www.mongodb.org](https://www.mongodb.org) - MongoDBis an open-source, document database designed for ease of development and scaling. 17 | * **Mongoose** [http://mongoosejs.com/](http://mongoosejs.com/) - elegant mongodb object modeling for node.js 18 | * **Redis** [http://redis.io/](http://redis.io/) - Redis is an open source, in-memory data structure store, used as database, cache and message broker. 19 | * **Swagger** [http://swagger.io/](http://swagger.io/) - The World's Most Popular Framework for APIs. 20 | * **Jason Web Token** [https://github.com/auth0/node-jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. 21 | * **NodeMailer** [http://nodemailer.com/](http://nodemailer.com/) - Send e-mails with Node.JS – easy as cake! 22 | * **BlazeMeter** [https://blazemeter.com/](https://blazemeter.com/) -Run massively scalable, open source-based performance tests against all of your apps, from classic 23 | web and mobile to microservices and APIs, and validate performance at every software delivery stage. 24 | * **JMeter** [http://jmeter.apache.org/]([http://jmeter.apache.org/) - The Apache JMeter™ application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance 25 | 26 | 27 | [![Join the chat at https://gitter.im/bartonhammond/snowflake](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bartonhammond/snowflake?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/bartonhammond/snowflake/blob/master/LICENSE) 28 | 29 | # Content 30 | - [Use case](#usecase) 31 | - [Summary](#summary) 32 | - [Setup](#setup) 33 | - [API Documentation](http://mysnowflake-bartonhammond.rhcloud.com/documentation) 34 | - [Source documentation](http://bartonhammond.github.io/snowflake-hapi-openshift/server.js.html) 35 | 36 | ## Usecase 37 | 38 | * **Register** - When user registers w/ email, username, password, the 39 | system sends a email verification. Until the user clicks the link 40 | within the email, their Email Verified field is false. When 41 | clicked, the field is set to true. The response from Register 42 | contains a "Session Token" for subsequent authentication. 43 | 44 | * **Login** When the user logs in with their username and password, 45 | the sytem responds with a "Session Token". 46 | 47 | * **Log Out** When the user logs out, the Session Token is blacklisted 48 | using Redis. Every entry point to the server that requires 49 | authentication first checks if the Session Toke has already been 50 | revoked by checking it's presence in Redis. If present, the request 51 | is denied. 52 | 53 | * **Reset Password** Once the user provides an Email address, they 54 | receive an email with a link that takes them to a form to submit a 55 | new password 56 | 57 | * **Profile** Once a user is logged in, they can view their profile. 58 | 59 | * **Profile Update** A User can modify their username and/or email. 60 | If the email address is modified, their Email Verified value is 61 | set again to false and a new 62 | email is sent to the new address for verification. 63 | 64 | 65 | ## Summary 66 | 67 | ### OpenShift 68 | OpenShift supports a free NodeJS setup that will scale with web 69 | traffic. This **Snowflake Server** setup will use MongoDB and Redis. 70 | ![Open Shift Dashboard](https://cloud.githubusercontent.com/assets/1282364/12080387/4f44e074-b21e-11e5-97d0-244573cc7460.png) 71 | * This server is using 3 small gears: 72 | * NodeJS (actually at version 4.2.3) 73 | * MongoDB 74 | * Redis 75 | * And a **Web Load Balancer** 76 | 77 | Some commands that you'll want to know about, once you've install the 78 | 'rhc' client: 79 | 80 | * rhc ssh -a mysnowflake 81 | 82 | You can check the performance of your application using the link 83 | 84 | ``` 85 | your-app-domain/haproxy-status/ 86 | ``` 87 | 88 | The HAProxy Status Page 89 | ![HAProxy](https://cloud.githubusercontent.com/assets/1282364/12080273/4f1806c4-b21b-11e5-8293-065ee2ab8ea7.png) 90 | 91 | 92 | ### Hapi 93 | The nodeJS server uses Hapi. Hapi was developed and Open Sourced by 94 | [Walmart Labs](http://www.walmartlabs.com/project_type/open-source/). 95 | It has been battle tested by Walmart, the largest retailer on earth. 96 | I chose it over Express 'cause Hapi is more targeted to API support 97 | and it looked interesting. 98 | 99 | This server is documented [here](http://bartonhammond.github.io/snowflake-hapi-openshift/server.js.html) in its entirety. 100 | 101 | Here's some flavor of what Hapi offers. Below is the declarative definition of the ```/account/login``` end point. The ```payload``` is validated here and shows how the ```username``` has a regex expression and is required. The same for the ```email```. The ```config``` option has the ```tags, description, notes``` that document how the ```api``` is used. The ```handler``` is defined elsewhere. Separating the end point validation and declaration from the implementation cleans up the code. 102 | 103 | ``` 104 | { 105 | method: 'POST', 106 | path: '/account/login', 107 | handler: AccountHandlers.loginUser, 108 | config: { 109 | // Include this API in swagger documentation 110 | tags: ['api'], 111 | description: 'A user can login', 112 | notes: 'The user login will return a sessionToken', 113 | validate: { 114 | payload: { 115 | //username required with same regex as client 116 | username: Joi.string().regex(CONFIG.validation.username).required(), 117 | //password required with same regex as client 118 | password: Joi.string().regex(CONFIG.validation.password).required() 119 | } 120 | } 121 | } 122 | }, 123 | ``` 124 | 125 | 126 | ### MongoDb 127 | Mongodb will host our documents, namely User information, at this 128 | time. We'll be using **Mongoose** for interacting with Mongo within 129 | our code. 130 | 131 | Once you're ssh'd into Openshift via ```rhc ssh -a mysnowflake```, you 132 | can use the ```mongo``` shell. 133 | 134 | 135 | ### Redis 136 | Redis is fantastic for key,value pair access. We're using it here for 137 | "Black Listing Json Web Tokens". You can read about this concept here [https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/](https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/) 138 | 139 | 140 | ### Swagger 141 | Swagger provides the api documentation - simply augmenting the 142 | endpoints generates a page showing all the API access points. 143 | 144 | Shown below is the generated API documentation - 145 | 146 | ![Swagger docs](https://cloud.githubusercontent.com/assets/1282364/12080462/67b642e0-b220-11e5-905e-0238432e496b.png) 147 | 148 | NOTE: you can test the APIs directly from the browser with the forms that Swagger provides! 149 | 150 | ![Swagger form](https://cloud.githubusercontent.com/assets/1282364/12080686/6d358390-b228-11e5-917e-23443bff39d6.png) 151 | 152 | ### Jason Web Token 153 | 154 | JWT is used in the Authentication as a Session Token. You can read the docs [here](http://bartonhammond.github.io/snowflake-hapi-openshift/src/auth/jwt-strategy.js.html) showing how it's setup. 155 | 156 | 157 | ### JMeter 158 | 159 | Using JMeter allowed me to performance test the API. I created a test suite with JMeter as shown below and debugged the script by running locally. Once I was satisfied, I changed the ```HTTP Request Defaults``` and uploaded to BlazeMeter for testing. 160 | 161 | Shown below is the script defined in JMeter 162 | 163 | ![JMeter setup](https://cloud.githubusercontent.com/assets/1282364/12080705/1b0c4ad0-b229-11e5-8690-08430d50b865.png) 164 | 165 | ### BlazeMeter 166 | BlazeMeter was used to perform the tests as it is much better equipped to host the threads then my personal mac. 167 | 168 | 169 | Running BlazeMeter 171 | 172 | 173 | The following screens show the results of running 50 concurrent users performing the following actions with a 1 second delay between each action: 174 | 175 | * **register** - Register with username, password, and email 176 | * **login** - Usename, password sued to login 177 | * **restricted access** - access a page requiring SessionToken 178 | * **profile/account/me** - show my profile 179 | * **profile/account/{_id}** - update the profile defined by _id 180 | 181 | **Original Test Configuration** 182 | ![Original Test Configuration](https://cloud.githubusercontent.com/assets/1282364/12080259/f004f1d8-b21a-11e5-8675-e81f4f915842.png) 183 | 184 | **Overview** 185 | ![Overview](https://cloud.githubusercontent.com/assets/1282364/12080257/effc7008-b21a-11e5-981f-97c59494ff16.png) 186 | 187 | **Timeline Report** 188 | ![Timeline Report](https://cloud.githubusercontent.com/assets/1282364/12080258/effeaddc-b21a-11e5-95c8-72e6d9960092.png) 189 | 190 | **Load Report** 191 | ![Load Report](https://cloud.githubusercontent.com/assets/1282364/12080260/f004d7e8-b21a-11e5-8c42-1763e2c83945.png) 192 | 193 | **Aggregate Report** 194 | ![Aggregate Report](https://cloud.githubusercontent.com/assets/1282364/12080261/f00705cc-b21a-11e5-8a33-c19a68c4abe1.png) 195 | 196 | **Monitoring Report** 197 | ![Monitoring Report](https://cloud.githubusercontent.com/assets/1282364/12080262/f0071224-b21a-11e5-82f7-c1f2dd5bfd58.png) 198 | 199 | ------------------- 200 | 201 | 202 | ## Setup 203 | 204 | Below are the instructions for setting up the server on your local machine. These instructions work fine on the Mac - no promise is made for other OSs. 205 | 206 | * **Pre-setup step**: We are using **NodeMailer** and if you use a Gmail account, be sure to follow the steps below from [https://github.com/nodemailer/nodemailer](https://github.com/nodemailer/nodemailer) 207 | 208 | 209 | > You may need to "Allow Less Secure Apps" in your gmail account (it's all the way at the bottom). You also may need to "Allow access to your Google account" 210 | 211 | 212 | 213 | ### Locally (one time only setup) 214 | ---------------------------------------------------------- 215 | * Install Mongo db 216 | [https://www.mongodb.org/downloads#production](https://www.mongodb.org/downloads#production) 217 | * to start, ```sudo mongod``` 218 | 219 | * Install Redis 220 | 221 | * I used Redis 2.18.24, which is what's installed on OpenShift 222 | * down load and unzip [http://download.redis.io/releases/redis-2.8.24.tar.gz](http://download.redis.io/releases/redis-2.8.24.tar.gz) 223 | 224 | ``` 225 | cd redis-2.8.24 226 | make 227 | cd src/ 228 | ./redis-server 229 | ``` 230 | 231 | * Update ip in config file w/ ip from `ifconfig` 232 | Example: 233 | ``` 234 | hapi: { 235 | port: 5000, 236 | ip: '192.168.0.5' 237 | }ik 238 | 239 | ``` 240 | 241 | * Update Snowflake ```src/lib/config.js``` w/ same ip from step above 242 | Example: 243 | ``` 244 | HAPI: { 245 | local: { 246 | url: 'http://192.168.0.5:5000' 247 | }, 248 | remote: { 249 | url: 'enter your remote url here' 250 | } 251 | } 252 | ``` 253 | 254 | * Then in different terminal, inside mysnowflake, 255 | 256 | ``` 257 | npm start 258 | ``` 259 | 260 | 261 | ### OpenShift 262 | 263 | Watch the following video to see all the steps to install the **Hapi nodes server to Openshift** in action: 264 | 265 | Snowflake Hot Loading 267 | 268 | 269 | ---------------------------------------------------------- 270 | 271 | * Create an account at http://openshift.redhat.com/ 272 | 273 | * Install the command line tool ```rhc``` 274 | 275 | * [https://developers.openshift.com/en/managing-client-tools.html](https://developers.openshift.com/en/managing-client-tools.html) 276 | 277 | * Create a namespace, if you haven't already done so via the web interface 278 | 279 | ``` 280 | rhc domain create 281 | ``` 282 | 283 | * Create a nodejs application with mongodb. The ```-s``` option is for "Scaling" 284 | 285 | ``` 286 | rhc app-create mysnowflake nodejs-0.10 mongodb-2.4 -s 287 | 288 | ``` 289 | 290 | * Note that if you get an error during this step, most likely it has to do with copying the Openshift GIT repository to your local system. What you can do is to your OpenShift account and use the link they provided for the **Source Code**. Just ```git clone xxx``` where xxx is the link you copied from Openshift. 291 | 292 | 293 | * This next command will load the Redis cartridge 294 | 295 | ``` 296 | rhc add-cartridge \ 297 | http://cartreflect-claytondev.rhcloud.com/reflect?github=transformatordesign/openshift-redis-cart \ 298 | -a mysnowflake 299 | ``` 300 | 301 | 302 | * These next few steps copy the SnowFlake server to your local Git repository from Openshift. 303 | 304 | ``` 305 | cd mysnowflake 306 | git remote add upstream -m master git://github.com/bartonhammond/snowflake-hapi-openshift.git 307 | git pull -s recursive -X theirs upstream master 308 | ``` 309 | 310 | * Copy config.sample.js to config.js and provide values 311 | 312 | ``` 313 | cp src/config.sample.js src/config.js 314 | ``` 315 | 316 | * Then push the repo to OpenShift 317 | 318 | ``` 319 | git push origin master 320 | ``` 321 | 322 | * **That's it!!!** 323 | 324 | * You can now checkout your application at:[ http://mysnowflake-$yournamespace.rhcloud.com](http://mysnowflake-$yournamespace.rhcloud.com/) 325 | 326 | 327 | * If you want to have this project also in your personal GitHub account also, follow these steps but be **WARNED**: unless you use a "Private" repository, your 328 | ```config.js``` will be visible. 329 | 330 | * https://forums.openshift.com/how-to-keep-a-github-repository-and-an-openshift-repository-in-sync 331 | -------------------------------------------------------------------------------- /src/docs/home.md: -------------------------------------------------------------------------------- 1 | # Home 2 | 3 | The quick brown paragraph jumps over the lazy lists. 4 | 5 | > "Hooray for block quotes!" 6 | > — Peter 7 | 8 | **Bold and a bulleted list:** 9 | 10 | - One fish 11 | - Two fish 12 | 13 | _Italics and an ordered list:_ 14 | 15 | 1. Red fish 16 | 2. Blue fish 17 | 18 | --- 19 | 20 | ## Indented code block: 21 | 22 | var name = 'Princess Bubblegum'; 23 | 24 | ## Generic code block (fenced): 25 | 26 | ``` 27 | var fs = require('fs'); 28 | fs.readFileSync('home.md', 'utf8'); 29 | ``` 30 | 31 | ## JavaScript code block (fenced): 32 | 33 | ```js 34 | var path = require('path'); 35 | path.join(__dirname, 'home.md'); 36 | ``` 37 | 38 | --- 39 | 40 | ## Tables? 41 | 42 | | Name | Value | 43 | |----------|-------| 44 | | Jake | true | 45 | | Finn | true | 46 | | Ice King | false | 47 | 48 | -------------------------------------------------------------------------------- /src/lib/Crypto.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # ErrorAlert.js 3 | * 4 | * This class uses a component which displays the appropriate alert 5 | * depending on the platform 6 | * 7 | * The main purpose here is to determine if there is an error and then 8 | * plucking off the message depending on the shape of the error object. 9 | */ 10 | 'use strict'; 11 | /** 12 | * ## Imports 13 | * 14 | */ 15 | // our configurations 16 | var Config = require('../config'), 17 | //the crypto library 18 | crypto = require('crypto'), 19 | //algorithm for encryption 20 | algorithm = 'aes-256-ctr', 21 | privateKey = Config.crypto.privateKey; 22 | 23 | 24 | /** 25 | * ### public decrypt 26 | * 27 | */ 28 | exports.decrypt = function(password) { 29 | return decrypt(password); 30 | }; 31 | /** 32 | * ### public encrypt 33 | * 34 | */ 35 | exports.encrypt = function(password) { 36 | return encrypt(password); 37 | }; 38 | 39 | /** 40 | * ## method to decrypt data(password) 41 | * 42 | */ 43 | function decrypt(password) { 44 | var decipher = crypto.createDecipher(algorithm, privateKey); 45 | var dec = decipher.update(password, 'hex', 'utf8'); 46 | dec += decipher.final('utf8'); 47 | return dec; 48 | } 49 | /** 50 | * ## method to encrypt data(password) 51 | * 52 | */ 53 | function encrypt(password) { 54 | var cipher = crypto.createCipher(algorithm, privateKey); 55 | var crypted = cipher.update(password, 'utf8', 'hex'); 56 | crypted += cipher.final('hex'); 57 | return crypted; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/lib/Mailer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # Mailer.js 3 | * 4 | * Create email and send content 5 | * 6 | * 7 | */ 8 | 'use strict'; 9 | /** 10 | * ## Imports 11 | * 12 | */ 13 | var CONFIG = require('../config'), 14 | // kind of like underscore, but specific to Hapi 15 | Hoek = require('hoek'), 16 | // the email library 17 | nodemailer = require("nodemailer"); 18 | 19 | /** 20 | * ## transporter 21 | * configure the email provider 22 | */ 23 | var transporter = nodemailer.createTransport({ 24 | service: "Gmail", 25 | auth: { 26 | user: CONFIG.email.username, 27 | pass: CONFIG.email.password 28 | } 29 | }); 30 | /** 31 | * ## getUrl 32 | * Local or OpenShift 33 | * 34 | */ 35 | function getUrl( ) { 36 | var url = ''; 37 | if (process.env.OPENSHIFT_APP_DNS) { 38 | url = 'https://' + process.env.OPENSHIFT_APP_DNS; 39 | } else { 40 | url = 'http://127.0.0.1:8080'; 41 | } 42 | return url; 43 | } 44 | /** 45 | * ## sendMailVerificationLink 46 | * 47 | * send email for verification of the account's email address 48 | * 49 | */ 50 | exports.sendMailVerificationLink = function(user,token) { 51 | var url = getUrl(); 52 | var from = CONFIG.email.accountName; 53 | var mailbody = "

Thanks for Registering on" 54 | + " " 55 | + CONFIG.email.accountName 56 | +"

Please verify your email by clicking on the" 57 | + " verification link below.
Verification Link

"; 62 | mail(from, user.email , "Account Verification", mailbody); 63 | }; 64 | /** 65 | * ## sendMailResetPassword 66 | * 67 | * Set email to user so they can reset their password 68 | * 69 | */ 70 | exports.sendMailResetPassword = function(user, token) { 71 | 72 | var url = getUrl(); 73 | var from = CONFIG.email.accountName; 74 | var mailbody = "

A reset password action has been requested from" 75 | + " " 76 | + CONFIG.email.accountName 77 | +"

Please click on the " 78 | + " reset password link below.
" 79 | + " The link is only available for 15 minutes.
" 80 | + "Reset Password Link

"; 85 | mail(from, user.email , "Reset Password", mailbody); 86 | }; 87 | /** 88 | * ## mail 89 | * 90 | * The main function, sends the email 91 | * 92 | * @param from who email is from 93 | * @param email who email is sent to 94 | * @param subject the subject of the email 95 | * @param mailbody the body of the email 96 | */ 97 | function mail(from, email, subject, mailbody){ 98 | var mailOptions = { 99 | from: from, // sender address 100 | to: email, // list of receivers 101 | subject: subject, // Subject line 102 | html: mailbody // html body 103 | }; 104 | //Send email 105 | if (!CONFIG.email.test) { 106 | transporter.sendMail(mailOptions, function(error) { 107 | Hoek.assert(!error,error); 108 | }); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/routes/account/endpoints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # ErrorAlert.js 3 | * 4 | * This class uses a component which displays the appropriate alert 5 | * depending on the platform 6 | * 7 | * The main purpose here is to determine if there is an error and then 8 | * plucking off the message depending on the shape of the error object. 9 | */ 10 | 'use strict'; 11 | /** 12 | * ## Imports 13 | * 14 | */ 15 | //Handle the endpoints 16 | var AccountHandlers = require('./handlers'), 17 | //The static configurations 18 | CONFIG = require('../../config'), 19 | //Joi is Hapi's validation library 20 | Joi = require('joi'); 21 | 22 | var internals = {}; 23 | /** 24 | * ## Set the method, path, and handler 25 | * 26 | * Note the account/logout requires authentication 27 | * 28 | * Note the validation of the account/register parameters 29 | * 30 | * Note account/register has same Regex expression as Snowflake client 31 | */ 32 | internals.endpoints = [ 33 | { 34 | method: 'POST', 35 | path: '/account/register', 36 | handler: AccountHandlers.registerUser, 37 | config: { 38 | // Include this API in swagger documentation 39 | tags: ['api'], 40 | description: 'Register user', 41 | notes: 'The user registration generates an email for verification', 42 | validate: { 43 | payload: { 44 | //username required with same regex as client 45 | username: Joi.string().regex(CONFIG.validation.username).required(), 46 | //password required with same regex as client 47 | password: Joi.string().regex(CONFIG.validation.password).required(), 48 | //email required 49 | email: Joi.string().email().required() 50 | } 51 | } 52 | } 53 | }, 54 | { 55 | method: 'POST', 56 | path: '/account/login', 57 | handler: AccountHandlers.loginUser, 58 | config: { 59 | // Include this API in swagger documentation 60 | tags: ['api'], 61 | description: 'A user can login', 62 | notes: 'The user login will return a sessionToken', 63 | validate: { 64 | payload: { 65 | //username required with same regex as client 66 | username: Joi.string().regex(CONFIG.validation.username).required(), 67 | //password required with same regex as client 68 | password: Joi.string().regex(CONFIG.validation.password).required() 69 | } 70 | } 71 | } 72 | }, 73 | { 74 | method: 'POST', 75 | path: '/account/logout', 76 | handler: AccountHandlers.logoutUser, 77 | config: { 78 | // Include this API in swagger documentation 79 | tags: ['api'], 80 | description: 'A user can logout', 81 | notes: 'A user may be already be logged in', 82 | //authorization optional 83 | auth: 'token', 84 | validate: { 85 | headers: Joi.object({ 86 | 'Authorization': Joi.string() 87 | }).unknown() 88 | } 89 | } 90 | }, 91 | { 92 | method: 'GET', 93 | path: '/account/verifyEmail/{token}', 94 | handler: AccountHandlers.verifyEmail, 95 | config: { 96 | // Include this API in swagger documentation 97 | tags: ['api'], 98 | description: 'Users email is verified', 99 | notes: 'User clicks link in email sent during registration', 100 | validate: { 101 | params: { 102 | token: Joi.string().required() 103 | } 104 | } 105 | } 106 | }, 107 | { 108 | method: 'POST', 109 | path: '/account/resetPasswordRequest', 110 | handler: AccountHandlers.resetPasswordRequest, 111 | config: { 112 | // Include this API in swagger documentation 113 | tags: ['api'], 114 | description: 'User requests to reset password', 115 | notes: 'Email is sent to email address provided', 116 | validate: { 117 | payload: { 118 | //email required 119 | email: Joi.string().email().required() 120 | } 121 | } 122 | 123 | } 124 | }, 125 | { 126 | //send html w/ password reset form 127 | method: 'GET', 128 | path: '/account/resetPassword/{token}', 129 | handler: AccountHandlers.displayResetPassword 130 | }, 131 | { 132 | method: 'POST', 133 | path: '/account/resetPassword', 134 | handler: AccountHandlers.resetPassword, 135 | config: { 136 | // Include this API in swagger documentation 137 | tags: ['api'], 138 | description: 'User posts new password', 139 | notes: 'Password form posts new password', 140 | validate: { 141 | payload: { 142 | //password required with same regex as client 143 | password: Joi.string().regex(CONFIG.validation.password).required(), 144 | //email required 145 | token: Joi.string().required() 146 | } 147 | } 148 | 149 | } 150 | }, 151 | { 152 | method: 'GET', 153 | path: '/account/profile/me', 154 | handler: AccountHandlers.getMyProfile, 155 | config: { 156 | auth: 'token', 157 | tags: ['api'], 158 | description: 'Get the current users profile', 159 | notes: 'The user has username, email and emailVerified', 160 | validate: { 161 | headers: Joi.object({ 162 | 'Authorization': Joi.string() 163 | }).unknown() 164 | } 165 | } 166 | }, 167 | { 168 | method: 'POST', 169 | path: '/account/profile/{_id}', 170 | handler: AccountHandlers.updateProfile, 171 | config: { 172 | auth: 'token', 173 | tags: ['api'], 174 | description: 'Update user profile', 175 | notes: 'User is able to change their username and email', 176 | validate: { 177 | payload: { 178 | //email required 179 | email: Joi.string().email().required(), 180 | //username required 181 | username: Joi.string().regex(CONFIG.validation.username).required() 182 | }, 183 | params: { 184 | _id: Joi.string().required() 185 | }, 186 | headers: Joi.object({ 187 | 'Authorization': Joi.string() 188 | }).unknown() 189 | } 190 | } 191 | }, 192 | 193 | ]; 194 | 195 | 196 | module.exports = internals; 197 | -------------------------------------------------------------------------------- /src/routes/account/handlers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # account/handlers.js 3 | * 4 | * This handles all the account actions 5 | * 6 | * 7 | */ 8 | 'use strict'; 9 | /** 10 | * ## Imports 11 | * 12 | */ 13 | //Boom is an abstraction over http error codes 14 | var Boom = require('boom'), 15 | // our configuration 16 | CONFIG = require('../../config'), 17 | // our encrpyt and decrypt 18 | Crypto = require('../../lib/Crypto'), 19 | // support for token signing and verification 20 | JasonWebToken = require('jsonwebtoken'), 21 | // the Hapi strategy for jwt 22 | JwtAuth = require('../../auth/jwt-strategy'), 23 | // how we email 24 | Mailer = require('../../lib/Mailer'), 25 | // time/date functions 26 | Moment = require('moment'), 27 | // the client for redis 28 | redisClient = require('../../database/redis'), 29 | // helper library 30 | _ = require('underscore'), 31 | // our user in mongodb 32 | User = require('../../database/models/User'); 33 | 34 | var internals = {}; 35 | /** 36 | * ## registerUser 37 | * 38 | * Encrypt the password and store the user 39 | */ 40 | internals.registerUser = function (req, reply) { 41 | console.log('registerUser',req.payload); 42 | req.payload.password = Crypto.encrypt(req.payload.password); 43 | req.payload.emailVerified = false; 44 | var user = new User(req.payload); 45 | 46 | //save the user w/ the encrypted password 47 | user.save(function (err, user) { 48 | if (err) { 49 | reply(Boom.conflict(err)); 50 | } else { 51 | var tokenData = { 52 | username: user.username, 53 | id: user._id 54 | }; 55 | // send an email verification with a JWT token 56 | Mailer.sendMailVerificationLink(user, 57 | JasonWebToken.sign(tokenData, 58 | CONFIG.crypto.privateKey)); 59 | 60 | /** user: 61 | register { _id: 56844c798d4dce65e2b45b6e, 62 | emailVerified: false, 63 | password: 'd5be02df44dafbbcfb', 64 | email: 'barton@acclivyx.com', 65 | username: 'barton', 66 | __v: 0 } 67 | */ 68 | //Let the user know they are registered 69 | //Note that the token is created only with the user._id since 70 | //the user can change their username & email 71 | //If the token embeds either of those fields, it becomes 72 | //an invalid token once the user changes those fields 73 | reply({ 74 | statusCode: 201, 75 | objectId: user._id, 76 | sessionToken: JwtAuth.createToken({ id: user._id}) 77 | }); 78 | } 79 | }); 80 | }; 81 | /** 82 | * ## loginUser 83 | * 84 | * Find the user by username, verify the password matches and return 85 | * the user 86 | * 87 | */ 88 | internals.loginUser = function (req, reply) { 89 | User.findOne({ username: req.payload.username }, function (err, user) { 90 | 91 | if (err) { 92 | reply(Boom.unauthorized('Authentication failed')); 93 | } 94 | 95 | if (_.isNull(user)) { 96 | reply(Boom.unauthorized('Authentication failed')); 97 | } else { 98 | if (Crypto.decrypt(user.password) != 99 | req.payload.password) { 100 | reply(Boom.unauthorized('Authentication failed')); 101 | } else { 102 | reply({ 103 | email: user.email, 104 | objectId: user._id, 105 | username: user.username, 106 | sessionToken: JwtAuth.createToken({ 107 | id: user._id 108 | }) 109 | });//reply 110 | } 111 | } 112 | 113 | }); 114 | }; 115 | /** 116 | * ## logoutUser 117 | * 118 | * Create a token blacklist with Redis 119 | * see: https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/ 120 | * 121 | */ 122 | internals.logoutUser = function(req, reply) { 123 | var headers = req.headers.authorization.split(' '); 124 | redisClient.set(headers[1], new Date()); 125 | reply({}); 126 | }; 127 | /** 128 | * ## verifyEmail 129 | * 130 | * If the token is verified, find the user using the decoded info 131 | * from the token. 132 | * 133 | * Set the emailVeried to true if user is found 134 | * 135 | */ 136 | internals.verifyEmail = function (req, reply) { 137 | JasonWebToken.verify(req.params.token, CONFIG.crypto.privateKey, function(err, decoded) { 138 | if(decoded === undefined) { 139 | return reply(Boom.forbidden("invalid verification link")); 140 | } 141 | 142 | User.findUserByIdAndUserName(decoded.id, decoded.username, function(err, user){ 143 | //oops, something wrong 144 | if (err) { 145 | return reply(Boom.badImplementation(err)); 146 | } 147 | 148 | //No user found for this link 149 | if (user === null) { 150 | return reply(Boom.forbidden("invalid verification link")); 151 | } 152 | 153 | //not sure if this is really an error... 154 | if (user.isVerified === true) { 155 | return reply(Boom.forbidden("account is already verified")); 156 | } 157 | 158 | //Everything is fine, update the users flag for emailVerified 159 | user.emailVerified = true; 160 | user.save(function(err){ 161 | if (err) { 162 | return reply(Boom.badImplementation(err)); 163 | } 164 | return reply("account sucessfully verified"); 165 | }); 166 | }) 167 | 168 | }); 169 | }; 170 | /** 171 | * ## resetPasswordRequest 172 | * 173 | */ 174 | internals.resetPasswordRequest = function (req, reply) { 175 | User.findUserByEmail(req.payload.email, function(err, user) { 176 | if (err) { 177 | return reply(Boom.badImplementation(err)); 178 | } 179 | 180 | 181 | //Provide no indication if user exists 182 | if (user) { 183 | var tokenData = { 184 | username: user.username, 185 | id: user._id 186 | }; 187 | 188 | //The token will have id & username encrypted 189 | Mailer.sendMailResetPassword( 190 | user, 191 | JasonWebToken.sign(tokenData, 192 | CONFIG.crypto.privateKey)); 193 | 194 | reply({}); 195 | } 196 | }); 197 | }; 198 | 199 | 200 | /** 201 | * Display the resetPassword form 202 | */ 203 | /** 204 | * ## Imports 205 | * 206 | */ 207 | internals.displayResetPassword = function (req, reply) { 208 | reply.view('resetpassword.html', {token: req.params.token}); 209 | }; 210 | 211 | /** 212 | * Update password of user 213 | */ 214 | /** 215 | * ## Imports 216 | * 217 | */ 218 | internals.resetPassword = function (req, reply) { 219 | 220 | JasonWebToken.verify(req.payload.token, CONFIG.crypto.privateKey, function(err, decoded) { 221 | 222 | if(decoded === undefined) { 223 | return reply(Boom.forbidden("invalid resetPassword link")); 224 | } 225 | 226 | //Must fit w/in time allocated 227 | var diff = Moment().diff(Moment(decoded.iat * 1000)); 228 | if (diff > CONFIG.crypto.tokenExpires) { 229 | return reply(Boom.unauthorized('reset password allowed time has past')); 230 | } 231 | 232 | User.findUserByIdAndUserName(decoded.id, decoded.username, function(err, user){ 233 | if (err) { 234 | return reply(Boom.badImplementation(err)); 235 | } 236 | if (user === null) { 237 | return reply(Boom.forbidden("invalid resetPassword link")); 238 | } 239 | 240 | user.password = Crypto.encrypt(req.payload.password); 241 | user.save(function(err){ 242 | if (err) { 243 | return reply(Boom.badImplementation(err)); 244 | } 245 | 246 | return reply("account password updated"); 247 | 248 | }); 249 | }) 250 | }); 251 | 252 | }; 253 | /** 254 | * ## getMyProfile 255 | * 256 | * We only get here through authentication 257 | * 258 | * note: the user is available from the credentials! 259 | */ 260 | internals.getMyProfile = function (req, reply) { 261 | reply({ 262 | objectId: req.auth.credentials._id, 263 | username: req.auth.credentials.username, 264 | email: req.auth.credentials.email, 265 | emailVerified: req.auth.credentials.emailVerified, 266 | sessionToken: req.headers.authorization.split(' ')[1] 267 | }); 268 | }; 269 | /** 270 | * ## getMyProfile 271 | * 272 | * We only get here through authentication 273 | * 274 | * note: the user is available from the credentials! 275 | */ 276 | internals.updateProfile = function (req, reply) { 277 | User.findById(req.params._id, function(err, user) { 278 | if (err) { 279 | return reply(Boom.badImplementation(err)); 280 | } 281 | 282 | 283 | //Provide no indication if user exists 284 | if (user) { 285 | user.username = req.payload.username; 286 | 287 | //If user changed email, it needs to be verified 288 | if (user.email !== req.payload.email) { 289 | user.emailVerified = false; 290 | } 291 | user.email = req.payload.email; 292 | 293 | 294 | user.save(function(err, updatedUser) { 295 | if (err) { 296 | return reply(Boom.conflict("User couldn't be saved.")); 297 | } 298 | 299 | //Send verification email if needed 300 | if (!updatedUser.emailVerified) { 301 | var tokenData = { 302 | username: user.username, 303 | id: user._id 304 | }; 305 | // send an email verification with a JWT token 306 | Mailer.sendMailVerificationLink(user, 307 | JasonWebToken.sign(tokenData, 308 | CONFIG.crypto.privateKey)); 309 | } 310 | reply({}); 311 | }); 312 | 313 | } 314 | }); 315 | }; 316 | 317 | module.exports = internals; 318 | -------------------------------------------------------------------------------- /src/routes/general/endpoints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # general/endpoints.js 3 | * 4 | * This supports a status and env request 5 | * 6 | */ 7 | 'use strict'; 8 | /** 9 | * ## Imports 10 | * 11 | */ 12 | var GeneralHandlers = require('./handlers'), 13 | internals = {}; 14 | /** 15 | * ## endpoints 16 | * 17 | * both are simple gets 18 | */ 19 | internals.endpoints = [ 20 | { 21 | method: 'GET', 22 | path: '/', 23 | handler: GeneralHandlers.index, 24 | config: { 25 | description: 'Get the default/home template.', 26 | notes: 'Renders the /docs/home.md file as HTML.', 27 | tags: ['api'] 28 | } 29 | }, 30 | { 31 | method: 'GET', 32 | path: '/status', 33 | handler: GeneralHandlers.status, 34 | config: { 35 | description: 'Show the status.', 36 | notes: 'renders json if server is running', 37 | tags: ['api'] 38 | } 39 | }, 40 | { 41 | method: 'GET', 42 | path: '/env', 43 | handler: GeneralHandlers.env, 44 | config: { 45 | description: 'Show the environment variables.', 46 | notes: 'Renders the variables known to the server', 47 | tags: ['api'] 48 | } 49 | } 50 | 51 | ]; 52 | 53 | module.exports = internals; 54 | -------------------------------------------------------------------------------- /src/routes/general/handlers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # general/handlers.js 3 | * 4 | * Simple display of status and the environment we're running in 5 | * 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * ## Declaration 11 | * 12 | */ 13 | var internals = {}; 14 | 15 | /** 16 | * ## status - are we alive? 17 | * 18 | */ 19 | internals.index = function (req, reply) { 20 | //src/docs/home.md is a markdown file 21 | reply.view('README.md'); 22 | }; 23 | /** 24 | * ## status - are we alive? 25 | * 26 | */ 27 | internals.status = function (req, reply) { 28 | reply( {"status": "ok"} ); 29 | }; 30 | 31 | /** 32 | * ## env - display the environment variables available 33 | * 34 | */ 35 | internals.env = function (req, reply) { 36 | var content = 'Node Version: ' + process.version + '\n
\n' + 37 | 'Env: {
\n
';
38 |   //  Add env entries.
39 |   for (var k in process.env) {
40 |     content += '   ' + k + ': ' + process.env[k] + '\n';
41 |   }
42 |   content += '}\n

\n'; 43 | reply('\n' + 44 | ' Node.js Process Env\n' + 45 | ' \n
\n' + content + '\n'); 46 | 47 | } 48 | 49 | module.exports = internals; 50 | -------------------------------------------------------------------------------- /src/routes/restricted/endpoints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # This is our one restricted endpoint (beside logout) 3 | * 4 | */ 5 | 'use strict'; 6 | 7 | /** 8 | * ## Imports 9 | * 10 | */ 11 | var RestrictedHandlers = require('./handlers'); 12 | 13 | var internals = {}; 14 | /** 15 | * ## endpoints 16 | * 17 | * note the config which means authentication is required to access 18 | * this end point 19 | * 20 | */ 21 | internals.endpoints = [ 22 | { 23 | method: 'GET', 24 | path: '/restricted', 25 | handler: RestrictedHandlers.restrictedArea, 26 | config: { 27 | auth: 'token' 28 | } 29 | } 30 | ]; 31 | 32 | module.exports = internals; 33 | -------------------------------------------------------------------------------- /src/routes/restricted/handlers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * # restricted/handlers.js 3 | * 4 | * Display simple message if user has access 5 | * 6 | */ 7 | 'use strict'; 8 | 9 | var internals = {}; 10 | /** 11 | * ## restrictedArea - you can only reach here if you've passed 12 | * authentication 13 | * 14 | * note: the user name is available from the credentials! 15 | */ 16 | internals.restrictedArea = function (req, reply) { 17 | reply({ message: req.auth.credentials.username + ', welcome to restricted area!' }); 18 | }; 19 | 20 | module.exports = internals; 21 | -------------------------------------------------------------------------------- /src/views/resetpassword.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Snowflake - Reset Password 5 | 6 | 7 | 8 |

Snowflake - Reset Password

9 |
10 | Password: 
11 | 12 | {{#if token}} 13 | 14 | {{/if}} 15 |
16 | 17 | 18 | 19 | --------------------------------------------------------------------------------