├── .circleci └── config.yml ├── .dockerignore ├── .eslintrc ├── .gitignore ├── .migration-lint-ignore.json ├── .nsprc ├── .travis.yml ├── AUTHORS ├── CHANGELOG-server.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile-build ├── Dockerfile-test ├── Gruntfile.js ├── LICENSE ├── README.md ├── bin ├── db_patcher.js ├── mem.js ├── metrics.js └── server.js ├── config ├── config.js └── index.js ├── db-server ├── index.js ├── lib │ ├── bufferize.js │ ├── error.js │ └── safeJsonFormatter.js └── test │ ├── .eslintrc │ ├── backend │ ├── db_tests.js │ ├── index.js │ └── remote.js │ ├── client-then.js │ ├── fake.js │ └── local │ ├── bufferize.js │ ├── error.js │ └── safeJsonFormatter.js ├── docs ├── API.md └── DB_API.md ├── grunttasks ├── bump.js ├── changelog.js ├── copyright.js ├── eslint.js ├── nsp.js └── version.js ├── index.js ├── lib ├── constants.js ├── db │ ├── mem.js │ ├── mysql.js │ ├── patch.js │ ├── random.js │ ├── schema │ │ ├── README.md │ │ ├── patch-000-001.sql │ │ ├── patch-001-000.sql │ │ ├── patch-001-002.sql │ │ ├── patch-002-001.sql │ │ ├── patch-002-003.sql │ │ ├── patch-003-002.sql │ │ ├── patch-003-004.sql │ │ ├── patch-004-003.sql │ │ ├── patch-004-005.sql │ │ ├── patch-005-004.sql │ │ ├── patch-005-006.sql │ │ ├── patch-006-005.sql │ │ ├── patch-006-007.sql │ │ ├── patch-007-006.sql │ │ ├── patch-007-008.sql │ │ ├── patch-008-007.sql │ │ ├── patch-008-009.sql │ │ ├── patch-009-008.sql │ │ ├── patch-009-010.sql │ │ ├── patch-010-009.sql │ │ ├── patch-010-011.sql │ │ ├── patch-011-010.sql │ │ ├── patch-011-012.sql │ │ ├── patch-012-011.sql │ │ ├── patch-012-013.sql │ │ ├── patch-013-012.sql │ │ ├── patch-013-014.sql │ │ ├── patch-014-013.sql │ │ ├── patch-014-015.sql │ │ ├── patch-015-014.sql │ │ ├── patch-015-016.sql │ │ ├── patch-016-015.sql │ │ ├── patch-016-017.sql │ │ ├── patch-017-016.sql │ │ ├── patch-017-018.sql │ │ ├── patch-018-017.sql │ │ ├── patch-018-019.sql │ │ ├── patch-019-018.sql │ │ ├── patch-019-020.sql │ │ ├── patch-020-019.sql │ │ ├── patch-020-021.sql │ │ ├── patch-021-020.sql │ │ ├── patch-021-022.sql │ │ ├── patch-022-021.sql │ │ ├── patch-022-023.sql │ │ ├── patch-023-022.sql │ │ ├── patch-023-024.sql │ │ ├── patch-024-023.sql │ │ ├── patch-024-025.sql │ │ ├── patch-025-024.sql │ │ ├── patch-025-026.sql │ │ ├── patch-026-025.sql │ │ ├── patch-026-027.sql │ │ ├── patch-027-026.sql │ │ ├── patch-027-028.sql │ │ ├── patch-028-027.sql │ │ ├── patch-028-029.sql │ │ ├── patch-029-028.sql │ │ ├── patch-029-30.sql │ │ ├── patch-030-029.sql │ │ ├── patch-030-031.sql │ │ ├── patch-031-030.sql │ │ ├── patch-031-032.sql │ │ ├── patch-032-031.sql │ │ ├── patch-032-033.sql │ │ ├── patch-033-032.sql │ │ ├── patch-033-034.sql │ │ ├── patch-034-033.sql │ │ ├── patch-034-035.sql │ │ ├── patch-035-034.sql │ │ ├── patch-035-036.sql │ │ ├── patch-036-035.sql │ │ ├── patch-036-037.sql │ │ ├── patch-037-036.sql │ │ ├── patch-037-038.sql │ │ ├── patch-038-037.sql │ │ ├── patch-038-039.sql │ │ ├── patch-039-038.sql │ │ ├── patch-039-040.sql │ │ ├── patch-040-039.sql │ │ ├── patch-040-041.sql │ │ ├── patch-041-040.sql │ │ ├── patch-041-042.sql │ │ ├── patch-042-041.sql │ │ ├── patch-042-043.sql │ │ ├── patch-043-042.sql │ │ ├── patch-043-044.sql │ │ ├── patch-044-043.sql │ │ ├── patch-044-045.sql │ │ ├── patch-045-044.sql │ │ ├── patch-045-046.sql │ │ ├── patch-046-045.sql │ │ ├── patch-046-047.sql │ │ ├── patch-047-046.sql │ │ ├── patch-047-048.sql │ │ ├── patch-048-047.sql │ │ ├── patch-048-049.sql │ │ ├── patch-049-048.sql │ │ ├── patch-049-050.sql │ │ ├── patch-050-049.sql │ │ ├── patch-050-051.sql │ │ ├── patch-051-050.sql │ │ ├── patch-051-052.sql │ │ ├── patch-052-051.sql │ │ ├── patch-052-053.sql │ │ ├── patch-053-052.sql │ │ ├── patch-053-054.sql │ │ ├── patch-054-053.sql │ │ ├── patch-054-055.sql │ │ ├── patch-055-054.sql │ │ ├── patch-055-056.sql │ │ ├── patch-056-055.sql │ │ ├── patch-056-057.sql │ │ ├── patch-057-056.sql │ │ ├── patch-057-058.sql │ │ ├── patch-058-057.sql │ │ ├── patch-058-059.sql │ │ ├── patch-059-058.sql │ │ ├── patch-059-060.sql │ │ ├── patch-060-059.sql │ │ ├── patch-060-061.sql │ │ ├── patch-061-060.sql │ │ ├── patch-061-062.sql │ │ ├── patch-062-061.sql │ │ ├── patch-062-063.sql │ │ ├── patch-063-062.sql │ │ ├── patch-063-064.sql │ │ ├── patch-064-063.sql │ │ ├── patch-064-065.sql │ │ ├── patch-065-064.sql │ │ ├── patch-065-066.sql │ │ ├── patch-066-065.sql │ │ ├── patch-066-067.sql │ │ ├── patch-067-066.sql │ │ ├── patch-067-068.sql │ │ ├── patch-068-067.sql │ │ ├── patch-068-069.sql │ │ ├── patch-069-068.sql │ │ ├── patch-069-070.sql │ │ ├── patch-070-069.sql │ │ ├── patch-070-071.sql │ │ ├── patch-071-070.sql │ │ ├── patch-071-072.sql │ │ ├── patch-072-071.sql │ │ ├── patch-072-073.sql │ │ ├── patch-073-072.sql │ │ ├── patch-073-074.sql │ │ ├── patch-074-073.sql │ │ ├── patch-074-075.sql │ │ ├── patch-075-074.sql │ │ ├── patch-075-076.sql │ │ ├── patch-076-075.sql │ │ ├── patch-076-077.sql │ │ ├── patch-077-076.sql │ │ ├── patch-077-078.sql │ │ ├── patch-078-077.sql │ │ ├── patch-078-079.sql │ │ ├── patch-079-078.sql │ │ ├── patch-079-080.sql │ │ ├── patch-080-079.sql │ │ ├── patch-080-081.sql │ │ ├── patch-081-080.sql │ │ ├── patch-081-082.sql │ │ ├── patch-082-081.sql │ │ ├── patch-082-083.sql │ │ ├── patch-083-082.sql │ │ ├── patch-083-084.sql │ │ ├── patch-084-083.sql │ │ ├── patch-084-085.sql │ │ ├── patch-085-084.sql │ │ ├── patch-085-086.sql │ │ ├── patch-086-085.sql │ │ ├── patch-086-087.sql │ │ ├── patch-087-086.sql │ │ ├── patch-087-088.sql │ │ ├── patch-088-087.sql │ │ ├── patch-088-089.sql │ │ ├── patch-089-088.sql │ │ ├── patch-089-090.sql │ │ ├── patch-090-089.sql │ │ ├── patch-090-091.sql │ │ ├── patch-091-090.sql │ │ ├── patch-091-092.sql │ │ ├── patch-092-091.sql │ │ ├── patch-092-093.sql │ │ ├── patch-093-092.sql │ │ ├── patch-093-094.sql │ │ ├── patch-094-093.sql │ │ ├── patch-094-095.sql │ │ ├── patch-095-094.sql │ │ ├── patch-095-096.sql │ │ ├── patch-096-095.sql │ │ ├── patch-096-097.sql │ │ └── patch-097-096.sql │ └── util.js ├── logging.js ├── newrelic.js └── promise.js ├── npm-shrinkwrap.json ├── package.json ├── scripts ├── .eslintrc ├── analyze-mx-stats.js ├── create-triggers-accounts.sh ├── create-triggers-accounts.sql ├── createAccounts.js ├── metrics-loop.sh ├── metrics-perf.sh ├── migration-lint.js ├── migration.sh ├── mocha-coverage.js ├── populate-session-tokens.js ├── rpm-version.js └── union-compare-two-tables.sql └── test ├── .eslintrc ├── backend ├── db_tests.js └── remote.js ├── db_server_stub.js ├── harness.js ├── lib └── log.js ├── local ├── constants_test.js ├── incorrect-patch-level.js ├── log-stats.js ├── metrics_tests.js ├── mysql_tests.js ├── prune_tokens.js ├── random.js └── utils.js └── mem ├── db_tests.js └── remote.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # These environment variables must be set in CircleCI UI 2 | # 3 | # DOCKERHUB_REPO - docker hub repo, format: / 4 | # DOCKER_EMAIL - login info for docker hub 5 | # DOCKER_USER 6 | # DOCKER_PASS 7 | # 8 | version: 2 9 | jobs: 10 | build: 11 | docker: 12 | - image: circleci/node:10 13 | steps: 14 | - checkout 15 | - setup_remote_docker 16 | 17 | - run: 18 | name: Create version.json 19 | command: > 20 | printf '{"version":{"hash":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}}\n' 21 | "$CIRCLE_SHA1" 22 | "$CIRCLE_TAG" 23 | "$CIRCLE_PROJECT_USERNAME" 24 | "$CIRCLE_PROJECT_REPONAME" 25 | "$CIRCLE_BUILD_URL" 26 | | tee config/version.json version.json 27 | - store_artifacts: 28 | path: version.json 29 | 30 | - run: 31 | name: Build deployment container image 32 | command: docker build -f Dockerfile-build -t fxa-auth-db-mysql:build . 33 | 34 | - run: 35 | name: Check npm install 36 | command: docker run --rm -it fxa-auth-db-mysql:build npm ls --production 37 | 38 | - run: 39 | name: Start mysql 40 | command: docker pull mysql/mysql-server:5.6 && docker run -d --name=mydb -e MYSQL_ALLOW_EMPTY_PASSWORD=true -e MYSQL_ROOT_HOST=% -p 3306:3306 mysql/mysql-server:5.6 41 | 42 | - run: 43 | name: Build test container image 44 | command: docker build -f Dockerfile-test -t fxa-auth-db-mysql:test . 45 | 46 | - run: 47 | name: Run Tests 48 | command: docker run --net="host" fxa-auth-db-mysql:test npm test 49 | 50 | - run: 51 | name: Push to Dockerhub 52 | command: | 53 | if [ "${CIRCLE_BRANCH}" == "master" ]; then 54 | DOCKER_TAG="latest" 55 | fi 56 | 57 | if [[ "${CIRCLE_BRANCH}" == feature* ]] || [[ "${CIRCLE_BRANCH}" == dockerpush* ]]; then 58 | DOCKER_TAG="${CIRCLE_BRANCH}" 59 | fi 60 | 61 | if [ -n "${CIRCLE_TAG}" ]; then 62 | DOCKER_TAG="$CIRCLE_TAG" 63 | fi 64 | 65 | if [ -n "${DOCKER_TAG}" ]; then 66 | echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin 67 | echo ${DOCKERHUB_REPO}:${DOCKER_TAG} 68 | docker tag fxa-auth-db-mysql:build ${DOCKERHUB_REPO}:${DOCKER_TAG} 69 | docker images 70 | docker push ${DOCKERHUB_REPO}:${DOCKER_TAG} 71 | fi 72 | 73 | workflows: 74 | version: 2 75 | 76 | # workflow jobs are _not_ run in tag builds by default 77 | # we use filters to whitelist jobs that should be run for tags 78 | 79 | # workflow jobs are run in _all_ branch builds by default 80 | # we use filters to blacklist jobs that shouldn't be run for a branch 81 | 82 | # see: https://circleci.com/docs/2.0/workflows/#git-tag-job-execution 83 | 84 | build-test-push: 85 | jobs: 86 | - build: 87 | filters: 88 | tags: 89 | only: /.*/ 90 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | plugins: 2 | - fxa 3 | extends: plugin:fxa/server 4 | 5 | rules: 6 | complexity: 0 7 | handle-callback-err: 0 8 | semi: [2, "never"] 9 | 10 | parserOptions: 11 | ecmaVersion: 2018 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config/dev.js 2 | db-server/node_modules 3 | node_modules/ 4 | sandbox/ 5 | coverage.html 6 | .nyc_output 7 | *.log 8 | *.swp 9 | *~ 10 | -------------------------------------------------------------------------------- /.migration-lint-ignore.json: -------------------------------------------------------------------------------- 1 | { 2 | "procedures": [ 3 | "accountEmails_4", 4 | "accountExists_2", 5 | "consumeRecoveryCode_2", 6 | "createAccount_7", 7 | "createEmail_2", 8 | "createEmailBounce_1", 9 | "deleteEmail_4", 10 | "emailRecord_4", 11 | "fetchEmailBounces_1", 12 | "fetchVerificationReminders_2", 13 | "getSecondaryEmail_1", 14 | "prune_7", 15 | "sessionWithDevice_15", 16 | "setPrimaryEmail_3" 17 | ], 18 | "tables": [ 19 | "deviceCommands", 20 | "securityEvents" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.nsprc: -------------------------------------------------------------------------------- 1 | { 2 | "exceptions": [] 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: "10" 4 | 5 | dist: trusty 6 | 7 | addons: 8 | apt: 9 | packages: 10 | - mysql-server-5.6 11 | - mysql-client-core-5.6 12 | - mysql-client-5.6 13 | 14 | services: 15 | - mysql 16 | 17 | notifications: 18 | email: 19 | - dcoates@mozilla.com 20 | - jrgm@mozilla.com 21 | - rfkelly@mozilla.com 22 | irc: 23 | channels: 24 | - "irc.mozilla.org#fxa-bots" 25 | use_notice: false 26 | skip_join: false 27 | 28 | before_install: 29 | - npm config set spin false 30 | 31 | before_script: 32 | - mysql -u root -e 'DROP DATABASE IF EXISTS fxa' 33 | - npm i grunt-cli -g 34 | - npm run outdated 35 | # HACK: ignore npm audit errors for now until we get them all fixed 36 | - npm audit || true 37 | 38 | script: 39 | - npm run test-travis 40 | - npm run migration-lint 41 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Chilton 2 | Danny Coates 3 | Edouard Oger 4 | Edouard Oger 5 | Greg Guthe 6 | Jamon Camisso 7 | Jamon Camisso 8 | John Morrison 9 | Jon Buckley 10 | Jon Buckley 11 | Michiel de Jong 12 | Peter deHaan 13 | Phil Booth 14 | Ryan Kelly 15 | Ryan Kelly 16 | Sai Pc 17 | Sean McArthur 18 | Shane Tomlinson 19 | Vijay Budhram 20 | Vijay Budhram 21 | Vlad Filippov 22 | vladikoff 23 | -------------------------------------------------------------------------------- /Dockerfile-build: -------------------------------------------------------------------------------- 1 | FROM node:10-alpine 2 | 3 | RUN npm install -g npm@6 && rm -rf ~app/.npm /tmp/* 4 | 5 | RUN apk add --no-cache make git gcc g++ python 6 | 7 | RUN addgroup -g 10001 app && \ 8 | adduser -D -G app -h /app -u 10001 app 9 | WORKDIR /app 10 | 11 | # S3 bucket in Cloud Services prod IAM 12 | ADD https://s3.amazonaws.com/dumb-init-dist/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init 13 | RUN chmod +x /usr/local/bin/dumb-init 14 | ENTRYPOINT ["/usr/local/bin/dumb-init", "--"] 15 | 16 | USER app 17 | 18 | COPY npm-shrinkwrap.json npm-shrinkwrap.json 19 | COPY package.json package.json 20 | 21 | RUN npm install --production && rm -rf ~app/.npm /tmp/* 22 | 23 | COPY . /app 24 | -------------------------------------------------------------------------------- /Dockerfile-test: -------------------------------------------------------------------------------- 1 | FROM fxa-auth-db-mysql:build 2 | RUN npm install 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | module.exports = function (grunt) { 6 | require('load-grunt-tasks')(grunt); 7 | 8 | grunt.loadTasks('grunttasks'); 9 | 10 | grunt.registerTask('default', ['eslint', 'copyright']); 11 | }; 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository has been migrated to https://github.com/mozilla/fxa/tree/master/packages/fxa-auth-db-mysql 2 | 3 | Please file issues and open pull requests against https://github.com/mozilla/fxa 4 | -------------------------------------------------------------------------------- /bin/db_patcher.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var path = require('path') 6 | var mysql = require('mysql') 7 | var config = require('../config') 8 | var logger = require('../lib/logging')('bin.db_patcher') 9 | var patcher = require('mysql-patcher') 10 | 11 | var patch = require('../lib/db/patch') 12 | const constants = require('../lib/constants') 13 | 14 | // set some options 15 | var options = Object.assign({}, config.master) 16 | options.dir = path.join(__dirname, '..', 'lib', 'db', 'schema') 17 | options.patchKey = config.patchKey 18 | options.metaTable = 'dbMetadata' 19 | options.patchLevel = patch.level 20 | options.mysql = mysql 21 | options.createDatabase = true 22 | options.reversePatchAllowed = false 23 | options.database = constants.DATABASE_NAME 24 | 25 | patcher.patch(options, function(err) { 26 | if (err) { 27 | logger.error('patch-error', { err : '' + err }) 28 | process.exit(2) 29 | } 30 | logger.info('patched', { level : options.patchLevel }) 31 | }) 32 | -------------------------------------------------------------------------------- /bin/mem.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var config = require('../config') 6 | var dbServer = require('../db-server') 7 | var error = dbServer.errors 8 | var logger = require('../lib/logging')('bin.server') 9 | var DB = require('../lib/db/mem')(logger, error) 10 | 11 | DB.connect(config) 12 | .done(function (db) { 13 | var server = dbServer.createServer(db) 14 | server.listen(config.port, config.hostname, function() { 15 | logger.info('start', { port : config.port }) 16 | }) 17 | server.on('error', function (err) { 18 | logger.error('start', { message: err.message }) 19 | }) 20 | server.on('success', function (d) { 21 | logger.info('summary', d) 22 | }) 23 | server.on('failure', function (err) { 24 | if (err.statusCode >= 500) { 25 | logger.error('summary', err) 26 | } 27 | else { 28 | logger.warn('summary', err) 29 | } 30 | }) 31 | server.on('mem', function (stats) { 32 | logger.info('mem', stats) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /bin/server.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | // This MUST be the first require in the program. 6 | // Only `require()` the newrelic module if explicity enabled. 7 | // If required, modules will be instrumented. 8 | require('../lib/newrelic')() 9 | 10 | var config = require('../config') 11 | var dbServer = require('../db-server') 12 | var error = dbServer.errors 13 | var logger = require('../lib/logging')('bin.server') 14 | var DB = require('../lib/db/mysql')(logger, error) 15 | var restify = require('restify') 16 | // configure Sentry 17 | var Raven = require('raven') 18 | const sentryDsn = config.sentryDsn 19 | 20 | if (sentryDsn) { 21 | Raven.config(sentryDsn, {}) 22 | logger.info('sentryEnabled') 23 | } else { 24 | logger.info('sentryDisabled') 25 | } 26 | 27 | function logCharsetInfo(db, poolName) { 28 | // Record some information about mysql connection configuration and 29 | // charset at startup. 30 | db._showVariables(poolName) 31 | .then( 32 | function(variables) { 33 | logger.info([ 'variables', poolName ].join('.'), variables) 34 | } 35 | ) 36 | .then( 37 | function() { 38 | return db._connectionConfig(poolName) 39 | } 40 | ) 41 | .then( 42 | function(config) { 43 | logger.info([ 'connectionConfig', poolName ].join('.'), config) 44 | } 45 | ).catch( 46 | function(err) { 47 | logger.error('error', { error: err }) 48 | } 49 | ) 50 | } 51 | 52 | DB.connect(config) 53 | .done(function (db) { 54 | // Serve the HTTP API. 55 | var server = dbServer.createServer(db) 56 | server.listen(config.port, config.hostname, function() { 57 | logger.info('start', { port : config.port }) 58 | }) 59 | 60 | server.on('uncaughtException', function (req, res, route, err) { 61 | if (sentryDsn) { 62 | Raven.captureException(err) 63 | } 64 | res.send(new restify.errors.InternalServerError('Server Error')) 65 | }) 66 | 67 | server.on('error', function (err) { 68 | if (sentryDsn) { 69 | Raven.captureException(err) 70 | } 71 | logger.error('start', { message: err.message }) 72 | }) 73 | server.on('success', function (d) { 74 | logger.info('summary', d) 75 | }) 76 | server.on('failure', function (err) { 77 | if (err.statusCode >= 500) { 78 | logger.error('summary', err) 79 | } 80 | else { 81 | logger.warn('summary', err) 82 | } 83 | }) 84 | server.on('mem', function (stats) { 85 | logger.info('mem', stats) 86 | }) 87 | 88 | // Log connection config and charset info 89 | logCharsetInfo(db, 'MASTER') 90 | logCharsetInfo(db, 'SLAVE') 91 | }) 92 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var fs = require('fs') 6 | var path = require('path') 7 | var url = require('url') 8 | var convict = require('convict') 9 | 10 | module.exports = require('./config')(fs, path, url, convict) 11 | -------------------------------------------------------------------------------- /db-server/lib/bufferize.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/ 6 | 7 | function unbuffer(object) { 8 | var keys = Object.keys(object) 9 | for (var i = 0; i < keys.length; i++) { 10 | var x = object[keys[i]] 11 | if (Buffer.isBuffer(x)) { 12 | object[keys[i]] = x.toString('hex') 13 | } 14 | } 15 | return object 16 | } 17 | 18 | function bufferize(object, onlyTheseKeys) { 19 | var keys = Object.keys(object) 20 | if (onlyTheseKeys) { 21 | keys = keys.filter(key => onlyTheseKeys.has(key)) 22 | } 23 | for (var i = 0; i < keys.length; i++) { 24 | var key = keys[i] 25 | var value = object[key] 26 | // Don't convert things with no value, but we still want 27 | // to bufferize falsy things like the empty string. 28 | if (typeof value !== 'undefined' && value !== null) { 29 | if (typeof value !== 'string' || ! HEX_STRING.test(value)) { 30 | throw new Error('Invalid hex data for ' + key + ': "' + value + '"') 31 | } 32 | object[key] = Buffer.from(value, 'hex') 33 | } 34 | } 35 | return object 36 | } 37 | 38 | function bufferizeRequest(keys, req, res, next) { 39 | try { 40 | if (req.body) { req.body = bufferize(req.body, keys) } 41 | if (req.params) { req.params = bufferize(req.params, keys) } 42 | } catch (err) { 43 | // Failure here means invalid hex data in a bufferized field. 44 | if (! err.statusCode) { 45 | err.statusCode = 400 46 | } 47 | return next(err) 48 | } 49 | return next() 50 | } 51 | 52 | function hexToUtf8(value) { 53 | return Buffer.from(value,'hex').toString('utf8') 54 | } 55 | 56 | module.exports = { 57 | unbuffer: unbuffer, 58 | bufferize: bufferize, 59 | bufferizeRequest: bufferizeRequest, 60 | hexToUtf8: hexToUtf8 61 | } 62 | -------------------------------------------------------------------------------- /db-server/lib/error.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var inherits = require('util').inherits 6 | 7 | function AppError(options) { 8 | this.message = options.message 9 | this.errno = options.errno 10 | this.error = options.error 11 | this.code = options.code 12 | if (options.stack) this.stack = options.stack 13 | } 14 | inherits(AppError, Error) 15 | 16 | AppError.prototype.toString = function () { 17 | return 'Error: ' + this.message 18 | } 19 | 20 | AppError.duplicate = function () { 21 | return new AppError( 22 | { 23 | code: 409, 24 | error: 'Conflict', 25 | errno: 101, 26 | message: 'Record already exists' 27 | } 28 | ) 29 | } 30 | 31 | AppError.notFound = function () { 32 | return new AppError( 33 | { 34 | code: 404, 35 | error: 'Not Found', 36 | errno: 116, 37 | message: 'Not Found' 38 | } 39 | ) 40 | } 41 | 42 | AppError.incorrectPassword = function () { 43 | return new AppError( 44 | { 45 | code: 400, 46 | error: 'Bad request', 47 | errno: 103, 48 | message: 'Incorrect password' 49 | } 50 | ) 51 | } 52 | 53 | AppError.cannotDeletePrimaryEmail = function () { 54 | return new AppError( 55 | { 56 | code: 400, 57 | error: 'Bad request', 58 | errno: 136, 59 | message: 'Can not delete primary email' 60 | } 61 | ) 62 | } 63 | 64 | AppError.expiredTokenVerificationCode = function () { 65 | return new AppError( 66 | { 67 | code: 400, 68 | error: 'Bad request', 69 | errno: 137, 70 | message: 'Expired token verification code' 71 | } 72 | ) 73 | } 74 | 75 | AppError.invalidVerificationMethod = function () { 76 | return new AppError( 77 | { 78 | code: 400, 79 | error: 'Bad request', 80 | errno: 138, 81 | message: 'Invalid verification method' 82 | } 83 | ) 84 | } 85 | 86 | AppError.recoveryKeyInvalid = () => { 87 | return new AppError({ 88 | code: 400, 89 | error: 'Bad Request', 90 | errno: 159, 91 | message: 'Recovery key is not valid' 92 | }) 93 | } 94 | 95 | AppError.cannotSetUnverifiedPrimaryEmail = function () { 96 | return new AppError( 97 | { 98 | code: 400, 99 | error: 'Bad request', 100 | errno: 147, 101 | message: 'Can not set unverified primary email' 102 | } 103 | ) 104 | } 105 | 106 | AppError.cannotSetUnownedPrimaryEmail = function () { 107 | return new AppError( 108 | { 109 | code: 400, 110 | error: 'Bad request', 111 | errno: 148, 112 | message: 'Can not set primary email to email that is not owned by account' 113 | } 114 | ) 115 | } 116 | 117 | AppError.wrap = function (err) { 118 | // Don't re-wrap! 119 | if (err instanceof AppError) { 120 | return err 121 | } 122 | return new AppError( 123 | { 124 | code: 500, 125 | error: 'Internal Server Error', 126 | errno: err.errno, 127 | message: err.code, 128 | stack: err.stack 129 | } 130 | ) 131 | } 132 | 133 | module.exports = AppError 134 | -------------------------------------------------------------------------------- /db-server/lib/safeJsonFormatter.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | module.exports = (req, res, body) => { 6 | let data = body ? JSON.stringify(body) : 'null' 7 | data = data.replace(//g, '\\u003e') 9 | .replace(/&/g, '\\u0026') 10 | 11 | res.setHeader('Content-Length', Buffer.byteLength(data)) 12 | return data 13 | } 14 | -------------------------------------------------------------------------------- /db-server/test/.eslintrc: -------------------------------------------------------------------------------- 1 | extends: ../../.eslintrc 2 | 3 | env: 4 | mocha: true 5 | 6 | rules: 7 | fxa/async-crypto-random: 0 8 | -------------------------------------------------------------------------------- /db-server/test/backend/index.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | module.exports = { 5 | dbTests: require('./db_tests'), 6 | remote: require('./remote'), 7 | } 8 | -------------------------------------------------------------------------------- /db-server/test/client-then.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | const clients = require('restify-clients') 5 | var P = require('../../lib/promise') 6 | 7 | var ops = [ 'head', 'get', 'post', 'put', 'del' ] 8 | 9 | module.exports = function createClient(cfg) { 10 | cfg.agent = false 11 | cfg.headers = { 12 | connection : 'close', 13 | } 14 | const client = clients.createJsonClient(cfg) 15 | 16 | // create a thenable version of each operation 17 | ops.forEach(function(name) { 18 | client[name + 'Then'] = function() { 19 | var p = P.defer() 20 | var args = Array.prototype.slice.call(arguments, 0) 21 | args.push(function(err, req, res, obj) { 22 | if (err) return p.reject(err) 23 | p.resolve({ req: req, res: res, obj: obj }) 24 | }) 25 | client[name].apply(this, args) 26 | return p.promise 27 | } 28 | }) 29 | 30 | return client 31 | } 32 | -------------------------------------------------------------------------------- /db-server/test/local/error.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | 'use strict' 5 | 6 | const assert = require('insist') 7 | 8 | describe('error', () => { 9 | it( 10 | 'error module', 11 | () => { 12 | const error = require('../../lib/error') 13 | assert.equal(typeof error, 'function', 'error module returns a function') 14 | 15 | const duplicate = error.duplicate() 16 | assert.equal(typeof duplicate, 'object', 'duplicate returns an object') 17 | assert(duplicate instanceof error, 'is an instance of error') 18 | assert.equal(duplicate.code, 409) 19 | assert.equal(duplicate.errno, 101) 20 | assert.equal(duplicate.message, 'Record already exists') 21 | assert.equal(duplicate.error, 'Conflict') 22 | assert.equal(duplicate.toString(), 'Error: Record already exists') 23 | 24 | const notFound = error.notFound() 25 | assert.equal(typeof notFound, 'object', 'notFound returns an object') 26 | assert(notFound instanceof error, 'is an instance of error') 27 | assert.equal(notFound.code, 404) 28 | assert.equal(notFound.errno, 116) 29 | assert.equal(notFound.message, 'Not Found') 30 | assert.equal(notFound.error, 'Not Found') 31 | assert.equal(notFound.toString(), 'Error: Not Found') 32 | 33 | const err = new Error('Something broke.') 34 | err.code = 'ER_QUERY_INTERRUPTED' 35 | err.errno = 1317 36 | const wrap = error.wrap(err) 37 | assert.equal(typeof wrap, 'object', 'wrap returns an object') 38 | assert(wrap instanceof error, 'is an instance of error') 39 | assert.equal(wrap.code, 500) 40 | assert.equal(wrap.errno, 1317) 41 | assert.equal(wrap.message, 'ER_QUERY_INTERRUPTED') 42 | assert.equal(wrap.error, 'Internal Server Error') 43 | assert.equal(wrap.toString(), 'Error: ER_QUERY_INTERRUPTED') 44 | } 45 | ) 46 | }) 47 | -------------------------------------------------------------------------------- /db-server/test/local/safeJsonFormatter.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | 'use strict' 5 | 6 | const assert = require('insist') 7 | const safeJsonFormatter = require('../../lib/safeJsonFormatter') 8 | 9 | describe('safeJsonFormatter module', () => { 10 | it('safeJsonFormatter function exported', () => { 11 | assert.equal(typeof safeJsonFormatter === 'function', true) 12 | }) 13 | 14 | it('escapes input', () => { 15 | const req = {} 16 | const res = { 17 | setHeader: () => { 18 | } 19 | } 20 | const body = {'foo': '