示例#1
0
v.addValidator = function(validator, key) {
    if (v[key] !== undefined) {
        throw new Error('[Validator] key: ' + key + '" has existed.');
    }
    v[key] = util.constant(validator);
};

v.addValidators({
    templateProp: v.object({
        message: v.string().required()
            .description('describe the meaning of key'),
        required: v.boolean().default(true),
        default: v.any().when('required', {
            is: false,
            then: v.required(),
            otherwise: v.optional(),
        }).description('the default value for error instance when `definition.required` is `OPTIONAL`'),
    }),
});

exports.check = function(value, schema) {
    var rst = v.validate(value, schema, {
        allowUnknown: true,
    });
    if (rst.error) {
        var message = 'wrong parameters of function\nInput Value: ' + rst.error.annotate();
        throw new Error(message);
    }
    return rst.value;
};
/* global __dirname */
'use strict';

const Hapi = require('hapi');
const Joi = require('joi');

const server = new Hapi.Server();

server.connection({
    host: 'localhost',
    port: Number(process.argv[2] || 8080)
});

server.route({
    path: '/chickens/{breed}',
    method: 'GET',
    handler: function(request, response) {
        response('You asked for the chickens ' + request.params.breed);
    },
    config: {
        validate: {
            params: {
                breed: Joi.required()
            }
        }
    }
});

server.start(function() {
    console.log('Server running at: ', server.info.uri);
});
示例#3
0
exports.register = function(server, options, next) {
  var Users = server.plugins.db.users

  function validateFunc(token, callback) {
    jwtVerifyAsync(token, APP_SECRET).then(function(data) {
      return Users.findById(data.userId).then(function(user) {
        if (user === null) return callback(null, false)
        callback(undefined, true, {
          user: user,
          scope: data.scope || []
        })
      }).catch(function(e) {
        callback(e)
      })
    }).catch(function() {
      callback(null, false)
    })
  }

  server.route({
    path: "/oauth/access_token",
    method: "POST",
    config: {
      tags: ["oauth"],
      validate: {
        payload: {
          grant_type: Joi.string().valid(
            "password", "refresh_token").required(),
          username: Joi.string().when("grant_type",
            {is: "password", then: Joi.required(), otherwise: Joi.forbidden()}),
          password: Joi.string().when("grant_type",
            {is: "password", then: Joi.required(), otherwise: Joi.forbidden()}),
          refresh_token: Joi.string().when("grant_type",
            {is: "refresh_token", then: Joi.required(), otherwise: Joi.forbidden()}),
          scope: Joi.string()
        }
      },
      response: {
        schema: {
          access_token: Joi.string().required(),
          token_type: Joi.string().valid("bearer").required(),
          expires_in: Joi.number().integer().required(),
          refresh_token: Joi.string().required(),
          scope: Joi.array().required()
        }
      },
      handler: function(req, rep) {
        function generateTokens(id, scope) {
          var access_token = jwt.sign(
            {userId: id, scope: scope, type: "access_token"},
            APP_SECRET,
            {expiresInSeconds: EXP_TIME}
          )
          var refresh_token = jwt.sign(
            {userId: id, scope: scope, type: "refresh_token"},
            APP_SECRET,
            {expiresInSeconds: EXP_TIME_REFRESH}
          )
          rep({
            access_token: access_token,
            token_type: "bearer",
            expires_in: EXP_TIME,
            refresh_token: refresh_token,
            scope: scope
          })
        }

        function doError(error) {
          var err = new Boom.badRequest()
          err.output.payload.error = error
          rep(err)
        }

        var scope = (function(s) {
          if (!_.isString(s)) return []
          return s.split(",")
        })(req.payload.scope)

        if (req.payload.grant_type == "password") {
          Users.findOne({where: {email: req.payload.username}}).then(function(user) {
            if (user === null) return doError("invalid_grant")
            return user.checkPassword(req.payload.password).then(function(ok) {
              if (ok) return generateTokens(user.id, scope)
              doError("invalid_grant")
            })
          }).catch(rep)
        } else if (req.payload.grant_type == "refresh_token"){
          jwtVerifyAsync(req.payload.refresh_token, APP_SECRET).then(function(data) {
            if (_.every(scope, function(s) {return _.includes(data.scope, s)})) {
              return generateTokens(data.userId, scope)
            }
            doError("invalid_scope")
          }).catch(jwt.TokenExpiredError, function() {
            doError("invalid_grant")
          })
        } else {
          doError("unsupported_grant_type")
        }
      }
    }
  })

  server.register(require("hapi-auth-bearer-token"), function(err) {
    if (err) return next(err)
    server.auth.strategy("oauth", "bearer-access-token", {validateFunc: validateFunc})
    next()
  })
}
示例#4
0
'use strict';

var Joi = require('joi');

var ConfigSchema = Joi.object().keys({
    agi: Joi.object().keys({
        port: Joi.number().integer().min(1).max(65535).required()
    }).required(),
    web: Joi.object().keys({
        port: Joi.number().integer().min(1).max(65535).required(),
        auth: Joi.boolean().valid(true, false).default(false),
        username: Joi.string()
            .when('auth', {is: true, then: Joi.required().default('admin')}),
        password: Joi.string()
            .when('auth', {is: true, then: Joi.required().default('password')}),
        realm: Joi.string()
            .when('auth', {is: true, then: Joi.default('voicer web')}),
    }).required(),    
    processing: Joi.object().keys({
        totalAttempts: Joi.number().min(1).max(20).default(2),
        playGreeting: Joi.boolean().default(true),
        playBeepBeforeRecording: Joi.boolean().default(true)
    }).required(),
    asterisk: Joi.object().keys({
        sounds: Joi.object().keys({
            onErrorBeforeFinish: Joi.string(),
            onErrorBeforeRepeat: Joi.string(),
            greeting: Joi.string()
        }).required(),
        recognitionDialplanVars: Joi.object().keys({
            status: Joi.string().required(),
示例#5
0
 joi$: function (schema, actmeta) {
   return schema.keys({b: Joi.required()})
 }
示例#6
0
/**
 * Created by mattiden on 06.06.15.
 */
var db = require('../../config/db');
var Joi = require('joi');
var bcrypt = require('bcrypt');

var signUpSchema = Joi.object().keys({
    email: Joi.string().email().required(),
    password: Joi.required(),
    firstname: Joi.string().required(),
    lastname: Joi.string().required()
});

module.exports = function(request, response){
    if(request.body.email && request.body.password){
            db.select()
                .from("Trainer")
                .where({email: request.body.email})
                .then(function(arrayWithExistingUser){
                    if(arrayWithExistingUser.length){
                        response.send({message: "User already exists."});
                        throw 0
                    }
                    else {
                        var password = bcrypt.hashSync(request.body.password, 10);
                        return db.insert({
                            firstname: request.body.firstname,
                            lastname: request.body.lastname,
                            password: password,
                            email: request.body.email
示例#7
0
module.exports = function(options) {
  var serverConfig = {
    debug: options.debug,
    connections: {
      routes: {
        security: true
      }
    }
  };

  if ( options.redisUrl ) {
    var redisUrl = require('redis-url').parse(options.redisUrl);
    serverConfig.cache = {
      engine: require('catbox-redis'),
      host: redisUrl.hostname,
      port: redisUrl.port,
      password: redisUrl.password
    };
  }

  var server = new Hapi.Server(serverConfig);

  server.connection({
    host: options.host,
    port: options.port
  });

  if ( options.logging ) {
    server.register({
      register: require('hapi-bunyan'),
      options: {
        logger: require('bunyan').createLogger({
          name: 'id-webmaker-org',
          level: options.logLevel
        })
      }
    }, function(err) {
      Hoek.assert(!err, err);
    });
  }

  server.register([
    require('hapi-auth-cookie'),
    require('inert'),
    require('scooter'),
    {
      register: require('blankie'),
      options: {
        defaultSrc: [
          '\'none\''
        ],
        styleSrc: [
          '\'self\'',
          'https://fonts.googleapis.com'
        ],
        imgSrc: [
          '\'self\'',
          'data:',
          'https://www.google-analytics.com',
          'http://www.google-analytics.com'
        ],
        scriptSrc: [
          '\'self\'',
          '\'unsafe-eval\'',
          'https://www.google-analytics.com',
          'http://www.google-analytics.com'
        ],
        fontSrc: [
          '\'self\'',
          'https://fonts.gstatic.com'
        ]
      }
    }
  ], function(err) {
    // MAYDAY, MAYDAY, MAYDAY!
    Hoek.assert(!err, err);

    server.auth.strategy('session', 'cookie', {
      password: options.cookieSecret,
      cookie: 'webmaker',
      ttl: 1000 * 60 * 60 * 24,
      isSecure: options.secureCookies,
      isHttpOnly: true
    });

    server.auth.default({
      strategy: 'session',
      mode: 'try'
    });
  });

  function skipCSRF(request, reply) {
    return true;
  }

  function isUniqueError(fieldName, err) {
    // SQLite and MariaDB/MySQL have conflicting error messages, and we don't know which DB the login server is using
    if (
      err &&
      // SQLite
      err.indexOf('Users.' + fieldName) !== -1 ||
      (
        // MariaDB/MySQL
        err.indexOf('ER_DUP_ENTRY') !== -1 &&
        err.indexOf(fieldName) !== -1
      )
    ) {
      return true;
    }
    return false;
  }

  server.register({
    register: require('crumb'),
    options: {
      restful: true,
      skip: !options.enableCSRF ? skipCSRF : undefined,
      cookieOptions: {
        isSecure: options.secureCookies
      }
    }
  }, function(err) {
    Hoek.assert(!err, err);
  });

  server.register({
    register: require('../lib/account'),
    options: {
      loginAPI: options.loginAPI,
      uri: options.uri
    }
  }, function(err) {
    Hoek.assert(!err, err);
  });

  var oauthDb = new OAuthDB(options.oauth_clients, options.authCodes, options.accessTokens);

  server.route([
    {
      method: 'GET',
      path: '/',
      handler: function(request, reply) {
        reply.redirect('/signup');
      }
    },
    {
      method: 'GET',
      path: '/{params*}',
      handler: {
        file: {
          path: Path.join(__dirname, '../public/index.html')
        }
      }
    },
    {
      method: 'GET',
      path: '/assets/{param*}',
      handler: {
        directory: {
          path: Path.join(__dirname, '../public')
        }
      }
    },
    {
      method: 'GET',
      path: '/login/oauth/authorize',
      config: {
        validate: {
          query: {
            client_id: Joi.string().required(),
            response_type: Joi.string().valid('code', 'token'),
            scopes: Joi.string().required(),
            state: Joi.string().required(),
            action: Joi.string().optional().valid('signup', 'signin').default('signin')
          }
        },
        pre: [
          {
            assign: 'user',
            method: function(request, reply) {
              if (request.auth.isAuthenticated) {
                return reply(request.auth.credentials);
              }

              var redirectUrl = '/login';
              if (request.query.action === 'signup') {
                redirectUrl = '/signup';
              }

              var redirect = url.parse(redirectUrl, true);
              redirect.query.client_id = request.query.client_id;
              redirect.query.response_type = request.query.response_type;
              redirect.query.state = request.query.state;
              redirect.query.scopes = request.query.scopes;

              reply().takeover().redirect(url.format(redirect));
            }
          },
          {
            assign: 'client',
            method: function(request, reply) {
              oauthDb.getClient(request.query.client_id, reply);
            }
          },
          {
            method: function(request, reply) {
              if (
                request.pre.client.allowed_responses.indexOf(request.query.response_type) === -1
              ) {
                return reply(Boom.forbidden('Response type forbidden: ' + request.query.response_type));
              }
              reply();
            }
          },
          {
            assign: 'scopes',
            method: function(request, reply) {
              reply(request.query.scopes.split(' '));
            }
          },
          {
            assign: 'auth_code',
            method: function(request, reply) {
              if (request.query.response_type !== 'code') {
                return reply(null);
              }

              oauthDb.generateAuthCode(
                request.pre.client.client_id,
                request.pre.user.id,
                request.pre.scopes,
                new Date(Date.now() + 60 * 1000).toISOString(),
                function(err, authCode) {
                  if (err) {
                    return reply(Boom.badRequest('An error occurred processing your request', err));
                  }

                  reply(authCode);
                }
              );
            }
          },
          {
            assign: 'access_token',
            method: function(request, reply) {
              if (request.query.response_type !== 'token') {
                return reply(null);
              }

              oauthDb.generateAccessToken(
                request.pre.client.client_id,
                request.pre.user.id,
                request.pre.scopes,
                reply
              );
            }
          }
        ]
      },
      handler: function(request, reply) {
        var redirectObj = url.parse(request.pre.client.redirect_uri, true);
        redirectObj.search = null;

        if (request.query.response_type === 'token') {
          redirectObj.hash = 'token=' + request.pre.access_token;
        } else {
          redirectObj.query.code = request.pre.auth_code;
          redirectObj.query.client_id = request.query.client_id;
        }
        redirectObj.query.state = request.query.state;

        reply.redirect(url.format(redirectObj));
      }
    },
    {
      method: 'POST',
      path: '/login/oauth/access_token',
      config: {
        validate: {
          payload: {
            grant_type: Joi.any().valid('authorization_code', 'password').required(),
            code: Joi.string().when('grant_type', {
              is: 'authorization_code',
              then: Joi.required(),
              otherwise: Joi.forbidden()
            }),
            client_secret: Joi.string().when('grant_type', {
              is: 'authorization_code',
              then: Joi.required(),
              otherwise: Joi.forbidden()
            }),
            client_id: Joi.string().required(),
            uid: Joi.string().when('grant_type', {
              is: 'password',
              then: Joi.required(),
              otherwise: Joi.forbidden()
            }),
            password: Joi.string().when('grant_type', {
              is: 'password',
              then: Joi.required(),
              otherwise: Joi.forbidden()
            }),
            scopes: Joi.string().when('grant_type', {
              is: 'password',
              then: Joi.required(),
              otherwise: Joi.forbidden()
            })
          },
          failAction: function(request, reply, source, error) {
            reply(Boom.badRequest('invalid ' + source + ': ' + error.data.details[0].path));
          }
        },
        auth: false,
        plugins: {
          crumb: false
        },
        pre: [
          {
            assign: 'grant_type',
            method: function (request, reply) {
              reply(request.payload.grant_type);
            }
          },
          {
            assign: 'client',
            method: function(request, reply) {
              oauthDb.getClient(request.payload.client_id, function(err, client) {
                if ( err ) {
                  return reply(err);
                }
                if (
                  client.allowed_grants.indexOf(request.pre.grant_type) === -1 ||
                  (
                    request.pre.grant_type === 'authorization_code' &&
                    client.client_secret !== request.payload.client_secret
                  )
                ) {
                  return reply(Boom.forbidden('Invalid Client Credentials'));
                }

                reply(client);
              });
            }
          },
          {
            assign: 'authCode',
            method: function(request, reply) {
              if ( request.pre.grant_type === 'password' ) {
                return server.methods.account.verifyPassword(request, function(err, json) {
                  if ( err ) {
                    return reply(err);
                  }

                  reply({
                    user_id: json.user.id,
                    scopes: request.payload.scopes.split(' ')
                  });
                });
              }
              oauthDb.verifyAuthCode(request.payload.code, request.pre.client.client_id, reply);
            }
          },
          {
            assign: 'accessToken',
            method: function(request, reply) {
              oauthDb.generateAccessToken(
                request.pre.client.client_id,
                request.pre.authCode.user_id,
                request.pre.authCode.scopes,
                reply
              );
            }
          }
        ]
      },
      handler: function(request, reply) {
        var responseObj = {
          access_token: request.pre.accessToken,
          scopes: request.pre.authCode.scopes,
          token_type: 'token'
        };

        reply(responseObj);
      }
    },
    {
      method: 'POST',
      path: '/login',
      config: {
        pre: [
          {
            assign: 'user',
            method: function(request, reply) {
              server.methods.account.verifyPassword(request, function(err, json) {
                if ( err ) {
                  return reply(err);
                }

                reply(json.user);
              });
            }
          }
        ]
      },
      handler: function(request, reply) {
        request.auth.session.set(request.pre.user);
        reply({ status: 'Logged In' });
      }
    },
    {
      method: 'POST',
      path: '/request-reset',
      config:{
        auth: false
      },
      handler: function(request, reply) {
        server.methods.account.requestReset(request, function(err, json) {
          if ( err ) {
            return reply(err);
          }

          reply(json);
        });
      }
    },
    {
      method: 'POST',
      path: '/reset-password',
      config:{
        auth: false
      },
      handler: function(request, reply) {
        server.methods.account.resetPassword(request, function(err, json) {
          if ( err ) {
            return reply(err);
          }

          reply(json);
        });
      }
    },
    {
      method: 'POST',
      path: '/create-user',
      config: {
        auth: false,
        plugins: {
          crumb: false
        },
        cors: true,
        validate: {
          payload: {
            username: Joi.string().regex(/^[a-zA-Z0-9\-]{1,20}$/).required(),
            email: Joi.string().email().required(),
            password: Joi.string().regex(/^\S{8,128}$/).required(),
            feedback: Joi.boolean().required(),
            client_id: Joi.string().required(),
            lang: Joi.string().default('en-US')
          },
          failAction: function(request, reply, source, error) {
            reply(Boom.badRequest('invalid ' + source + ': ' + error.data.details[0].path));
          }
        },
        pre: [
          {
            assign: 'username',
            method: function(request, reply) {
              reply(request.payload.username);
            }
          },
          {
            assign: 'password',
            method: function(request, reply) {
              var password = request.payload.password;
              var result = passTest.test(password);

              if ( !result.strong ) {
                var err = Boom.badRequest('Password not strong enough.', result);
                err.output.payload.details = err.data;
                return reply(err);
              }

              reply(password);
            }
          },
          {
            assign: 'client',
            method: function(request, reply) {
              oauthDb.getClient(request.payload.client_id, reply);
            }
          }
        ]
      },
      handler: function(request, reply) {
        server.methods.account.createUser(request, function(err, json) {
          if ( err ) {
            err.output.payload.data = err.data;
            return reply(err);
          }
          if ( json.login_error ) {
            if ( isUniqueError('username', json.login_error) ) {
              return reply(Boom.badRequest('That username is taken'));
            } else if ( isUniqueError('email', json.login_error) ) {
              return reply(Boom.badRequest('An account exists for that email address'));
            }
            return reply(Boom.badRequest(json.login_error));
          }
          request.auth.session.set(json.user);
          reply(json.user);
        });
      }
    },
    {
      method: 'GET',
      path: '/logout',
      config: {
        auth: false,
        pre: [
          {
            assign: 'redirectUri',
            method: function(request, reply) {
              if ( !request.query.client_id ) {
                return reply('https://webmaker.org');
              }
              oauthDb.getClient(request.query.client_id, function(err, client) {
                if ( err ) {
                  return reply(err);
                }
                reply(client.redirect_uri);
              });
            }
          }
        ]
      },
      handler: function(request, reply) {
        request.auth.session.clear();

        var redirectObj = url.parse(request.pre.redirectUri, true);
        redirectObj.query.logout = true;
        reply.redirect(url.format(redirectObj))
          .header('cache-control', 'no-cache');
      }
    },
    {
      method: 'GET',
      path: '/user',
      config: {
        auth: false,
        cors: true,
        pre: [
          {
            assign: 'requestToken',
            method: function(request, reply) {
              var tokenHeader = request.headers.authorization || '';
              tokenHeader = tokenHeader.split(' ');

              if ( tokenHeader[0] !== 'token' || !tokenHeader[1] ) {
                return reply(Boom.unauthorized('Missing or invalid authorization header'));
              }

              reply(tokenHeader[1]);
            }
          },
          {
            assign: 'token',
            method: function(request, reply) {
              oauthDb.lookupAccessToken(request.pre.requestToken, function(err, token) {
                if ( err ) {
                  return reply(err);
                }

                if ( token.expires_at <= Date.now() ) {
                  return reply(Boom.unauthorized('Expired token'));
                }

                var tokenScopes = token.scopes;

                if ( tokenScopes.indexOf('user') === -1 && tokenScopes.indexOf('email') === -1 ) {
                  return reply(Boom.unauthorized('The token does not have the required scopes'));
                }

                reply(token);
              });
            }
          },
          {
            assign: 'user',
            method: function(request, reply) {
              server.methods.account.getUser(request.pre.token.user_id, function(err, json) {
                if ( err ) {
                  return reply(Boom.badImplementation(err));
                }
                reply(json.user);
              });
            }
          }
        ]
      },
      handler: function(request, reply) {
        var responseObj = Scopes.filterUserForScopes(
          request.pre.user,
          request.pre.token.scopes
        );

        reply(responseObj);
      }
    },
    {
      method: 'POST',
      path: '/request-migration-email',
      config: {
        auth: false
      },
      handler: function(request, reply) {
        server.methods.account.requestMigrateEmail(request, function(err, json) {
          if ( err ) {
            return reply(Boom.badImplementation(err));
          }
          reply({ status: 'migration email sent' });
        });
      }
    },
    {
      method: 'POST',
      path: '/migrate-user',
      config: {
        auth: false,
        pre: [
          {
            assign: 'uid',
            method: function(request, reply) {
              reply(request.payload.uid);
            }
          },
          {
            assign: 'password',
            method: function(request, reply) {
              var password = request.payload.password;
              if ( !password ) {
                return reply(Boom.badRequest('No password provided'));
              }

              var result = passTest.test(password);

              if ( !result.strong ) {
                return reply(Boom.badRequest('Password not strong enough'), result);
              }

              reply(password);
            }
          },
          {
            assign: 'isValidToken',
            method: function(request, reply) {
              server.methods.account.verifyToken(request, function(err, json) {
                if ( err ) {
                  return reply(err);
                }

                reply(true);
              });
            }
          },
          {
            assign: 'user',
            method: function(request, reply) {
              server.methods.account.setPassword(
                request,
                request.pre.uid,
                request.pre.password,
                function(err, json) {
                  if ( err ) {
                    return reply(err);
                  }

                  reply(json.user);
                }
              );
            }
          }
        ]
      },
      handler: function(request, reply) {
        request.auth.session.set(request.pre.user);
        reply({ status: 'Logged in' });
      }
    },
    {
      method: 'POST',
      path: '/check-username',
      config: {
        auth: false
      },
      handler: function(request, reply) {
        server.methods.account.checkUsername(request, function(err, json) {
          if ( err ) {
            return reply(err);
          }

          reply(json);
        });
      }
    }
  ]);

  return server;
};
module.exports = (log, db, config, customs, push, pushbox, devices, oauthdb) => {
  // Loads and compiles a json validator for the payloads received
  // in /account/devices/notify
  const validatePushSchema = JSON.parse(fs.readFileSync(PUSH_PAYLOADS_SCHEMA_PATH));
  const validatePushPayloadAjv = ajv.compile(validatePushSchema);
  const { supportedLanguages, defaultLanguage } = config.i18n;
  const localizeTimestamp = require('fxa-shared').l10n.localizeTimestamp({
    supportedLanguages,
    defaultLanguage
  });
  const earliestSaneTimestamp = config.lastAccessTimeUpdates.earliestSaneTimestamp;

  function validatePushPayload(payload, endpoint) {
    if (endpoint === 'accountVerify') {
      if (isEmpty(payload)) {
        return true;
      }
      return false;
    }

    return validatePushPayloadAjv(payload);
  }

  function isEmpty(payload) {
    return payload && Object.keys(payload).length === 0;
  }

  function marshallLastAccessTime (lastAccessTime, request) {
    const languages = request.app.acceptLanguage;
    const result = {
      lastAccessTime,
      lastAccessTimeFormatted: localizeTimestamp.format(lastAccessTime, languages),
    };

    if (lastAccessTime < earliestSaneTimestamp) {
      // Values older than earliestSaneTimestamp are probably wrong.
      // Signal that to the front end so that it can fall back to
      // an approximate string like "last sync over 2 months ago".
      // And do it using additional properties so we don't affect
      // older content servers that are unfamiliar with the change.
      result.approximateLastAccessTime = earliestSaneTimestamp;
      result.approximateLastAccessTimeFormatted = localizeTimestamp.format(earliestSaneTimestamp, languages);
    }

    return result;
  }

  function marshallLocation (location, request) {
    let language;

    if (! location) {
      // Shortcut the error logging if location isn't set
      return {};
    }

    try {
      const languages = i18n.parseAcceptLanguage(request.app.acceptLanguage);
      language = i18n.bestLanguage(languages, supportedLanguages, defaultLanguage);

      if (language[0] === 'e' && language[1] === 'n') {
        // For English, return all of the location components
        return {
          city: location.city,
          country: location.country,
          state: location.state,
          stateCode: location.stateCode
        };
      }

      // For other languages, only return what we can translate
      const territories = require(`cldr-localenames-full/main/${language}/territories.json`);
      return {
        country: territories.main[language].localeDisplayNames.territories[location.countryCode]
      };
    } catch (err) {
      log.warn('devices.marshallLocation.warning', {
        err: err.message,
        languages: request.app.acceptLanguage,
        language,
        location
      });
    }

    // If something failed, don't return location
    return {};
  }

  // Creates a "full" device response, provided a credentials object and an optional
  // updated DB device record.
  function buildDeviceResponse(credentials, device = null) {
    // We must respond with the full device record,
    // including any default values for missing fields.
    return {
      // These properties can be picked from sessionToken or device as appropriate.
      pushCallback: credentials.deviceCallbackURL,
      pushPublicKey: credentials.deviceCallbackPublicKey,
      pushAuthKey: credentials.deviceCallbackAuthKey,
      pushEndpointExpired: !! credentials.deviceCallbackIsExpired,
      ...device,
      // But these need to be non-falsey, using default fallbacks if necessary
      id: (device && device.id) || credentials.deviceId,
      name: (device && device.name) || credentials.deviceName || devices.synthesizeName(credentials),
      type: (device && device.type) || credentials.deviceType || 'desktop',
      availableCommands: (device && device.availableCommands) || credentials.deviceAvailableCommands || {},
    };
  }

  return [
    {
      method: 'POST',
      path: '/account/device',
      options: {
        auth: {
          strategies: [
            'sessionToken',
            'refreshToken'
          ]
        },
        validate: {
          payload: isA.object({
            id: DEVICES_SCHEMA.id.optional(),
            name: DEVICES_SCHEMA.name.optional(),
            type: DEVICES_SCHEMA.type.optional(),
            pushCallback: DEVICES_SCHEMA.pushCallback.optional(),
            pushPublicKey: DEVICES_SCHEMA.pushPublicKey.optional(),
            pushAuthKey: DEVICES_SCHEMA.pushAuthKey.optional(),
            availableCommands: DEVICES_SCHEMA.availableCommands.optional(),
            // Some versions of desktop firefox send a zero-length
            // "capabilities" array, for historical reasons.
            // We accept but ignore it.
            capabilities: isA.array().length(0).optional()
          })
          .and('pushCallback', 'pushPublicKey', 'pushAuthKey')
        },
        response: {
          schema: isA.object({
            id: DEVICES_SCHEMA.id.required(),
            createdAt: isA.number().positive().optional(),
            name: DEVICES_SCHEMA.nameResponse.optional(),
            type: DEVICES_SCHEMA.type.optional(),
            pushCallback: DEVICES_SCHEMA.pushCallback.optional(),
            pushPublicKey: DEVICES_SCHEMA.pushPublicKey.optional(),
            pushAuthKey: DEVICES_SCHEMA.pushAuthKey.optional(),
            pushEndpointExpired: DEVICES_SCHEMA.pushEndpointExpired.optional(),
            availableCommands: DEVICES_SCHEMA.availableCommands.optional(),
          }).and('pushCallback', 'pushPublicKey', 'pushAuthKey')
        }
      },
      handler: async function (request) {
        log.begin('Account.device', request);

        const payload = request.payload;
        const credentials = request.auth.credentials;

        // Remove obsolete field, so we don't try to echo it back to the client.
        delete payload.capabilities;

        // Some additional, slightly tricky validation to detect bad public keys.
        if (payload.pushPublicKey && ! push.isValidPublicKey(payload.pushPublicKey)) {
          throw error.invalidRequestParameter('invalid pushPublicKey');
        }

        if (payload.id) {
          // Don't write out the update if nothing has actually changed.
          if (devices.isSpuriousUpdate(payload, credentials)) {
            return buildDeviceResponse(credentials);
          }

          // We also reserve the right to disable updates until
          // we're confident clients are behaving correctly.
          if (config.deviceUpdatesEnabled === false) {
            throw error.featureNotEnabled();
          }
        } else if (credentials.deviceId) {
          // Keep the old id, which is probably from a synthesized device record
          payload.id = credentials.deviceId;
        }

        const pushEndpointOk = ! payload.id || // New device.
                               (payload.id && payload.pushCallback &&
                                payload.pushCallback !== credentials.deviceCallbackURL); // Updating the pushCallback
        if (pushEndpointOk) {
          payload.pushEndpointExpired = false;
        }

        // We're doing a gradual rollout of the 'device commands' feature
        // in support of pushbox, so accept an 'availableCommands' field
        // if pushbox is enabled.
        if (payload.availableCommands && ! config.pushbox.enabled) {
            payload.availableCommands = {};
        }

        const device = await devices.upsert(request, credentials, payload);
        return buildDeviceResponse(credentials, device);
      }
    },
    {
      method: 'GET',
      path: '/account/device/commands',
      options: {
        validate: {
          query: {
            index: isA.number().optional(),
            limit: isA.number().optional().min(0).max(100).default(100),
          }
        },
        auth: {
          strategies: [
            'sessionToken',
            'refreshToken'
          ]
        },
        response: {
          schema: isA.object({
            index: isA.number().required(),
            last: isA.boolean().optional(),
            messages: isA.array().items(isA.object({
              index: isA.number().required(),
              data: isA.object({
                command: isA.string().max(255).required(),
                payload: isA.object().required(),
                sender: DEVICES_SCHEMA.id.optional()
              }).required()
            })).optional()
          }).and('last', 'messages')
        }
      },
      handler: async function (request) {
        log.begin('Account.deviceCommands', request);

        const sessionToken = request.auth.credentials;
        const uid = sessionToken.uid;
        const deviceId = sessionToken.deviceId;
        const query = request.query || {};
        const {index, limit} = query;

        return pushbox.retrieve(uid, deviceId, limit, index)
          .then(resp => {
            log.info('commands.fetch', { resp: resp });
            return resp;
          });
      }
    },
    {
      method: 'POST',
      path: '/account/devices/invoke_command',
      options: {
        auth: {
          strategies: [
            'sessionToken',
            'refreshToken'
          ]
        },
        validate: {
          payload: {
            target: DEVICES_SCHEMA.id.required(),
            command: isA.string().required(),
            payload: isA.object().required(),
            ttl: isA.number().integer().min(0).max(10000000).optional()
          }
        },
        response: {
          schema: {}
        }
      },
      handler: async function (request) {
        log.begin('Account.invokeDeviceCommand', request);

        const {target, command, payload} = request.payload;
        let {ttl} = request.payload;
        const sessionToken = request.auth.credentials;
        const uid = sessionToken.uid;
        const sender = sessionToken.deviceId;

        return customs.checkAuthenticated(request, uid, 'invokeDeviceCommand')
          .then(() => db.device(uid, target))
          .then(device => {
            if (! device.availableCommands.hasOwnProperty(command)) {
              throw error.unavailableDeviceCommand();
            }
            // 0 is perfectly acceptable TTL, hence the strict equality check.
            if (ttl === undefined && DEFAULT_COMMAND_TTL.has(command)) {
              ttl = DEFAULT_COMMAND_TTL.get(command);
            }
            const data = {
              command,
              payload,
              sender,
            };
            return pushbox.store(uid, device.id, data, ttl)
              .then(({index}) => {
                const url = new URL('v1/account/device/commands', config.publicUrl);
                url.searchParams.set('index', index);
                url.searchParams.set('limit', 1);
                return push.notifyCommandReceived(uid, device, command, sender, index, url.href, ttl);
              });
          })
          .then(() => { return {}; });
      }
    },
    {
      method: 'POST',
      path: '/account/devices/notify',
      options: {
        auth: {
          strategies: [
            'sessionToken',
            'refreshToken'
          ]
        },
        validate: {
          payload: isA.alternatives().try(
            isA.object({
              to: isA.string().valid('all').required(),
              _endpointAction: isA.string().valid('accountVerify').optional(),
              excluded: isA.array().items(isA.string().length(32).regex(HEX_STRING)).optional(),
              payload: isA.object().when('_endpointAction', { is: 'accountVerify', then: isA.required(), otherwise: isA.required() }),
              TTL: isA.number().integer().min(0).optional()
            }),
            isA.object({
              to: isA.array().items(isA.string().length(32).regex(HEX_STRING)).required(),
              _endpointAction: isA.string().valid('accountVerify').optional(),
              payload: isA.object().when('_endpointAction', { is: 'accountVerify', then: isA.required(), otherwise: isA.required() }),
              TTL: isA.number().integer().min(0).optional()
            })
          )
        },
        response: {
          schema: {}
        }
      },
      handler: async function (request) {
        log.begin('Account.devicesNotify', request);

        // We reserve the right to disable notifications until
        // we're confident clients are behaving correctly.
        if (config.deviceNotificationsEnabled === false) {
          throw error.featureNotEnabled();
        }

        const body = request.payload;
        const sessionToken = request.auth.credentials;
        const uid = sessionToken.uid;
        const payload = body.payload;
        const endpointAction = body._endpointAction || 'devicesNotify';


        if (! validatePushPayload(payload, endpointAction)) {
          throw error.invalidRequestParameter('invalid payload');
        }

        const pushOptions = {
          data: payload
        };

        if (body.TTL) {
          pushOptions.TTL = body.TTL;
        }

        return customs.checkAuthenticated(request, uid, endpointAction)
          .then(() => request.app.devices)
          .then(devices => {
            if (body.to !== 'all') {
              const include = new Set(body.to);
              devices = devices.filter(device => include.has(device.id));

              if (devices.length === 0) {
                log.error('Account.devicesNotify', {
                  uid: uid,
                  error: 'devices empty'
                });
                return;
              }
            } else if (body.excluded) {
              const exclude = new Set(body.excluded);
              devices = devices.filter(device => ! exclude.has(device.id));
            }

            return push.sendPush(uid, devices, endpointAction, pushOptions)
              .catch(catchPushError);
          })
          .then(() => {
            // Emit a metrics event for when a user sends tabs between devices.
            // In the future we will aim to get this event directly from sync telemetry,
            // but we're doing it here for now as a quick way to get metrics on the feature.
            if (
              payload &&
              payload.command === 'sync:collection_changed' &&
              // Note that payload schema validation ensures that these properties exist.
              payload.data.collections.length === 1 &&
              payload.data.collections[0] === 'clients'
            ) {
              let deviceId = undefined;

              if (sessionToken.deviceId) {
                deviceId = sessionToken.deviceId;
              }

              return request.emitMetricsEvent('sync.sentTabToDevice', {
                device_id: deviceId,
                service: 'sync',
                uid: uid
              });
            }
          })
          .then(() => { return {}; });

        function catchPushError (err) {
          // push may fail due to not found devices or a bad push action
          // log the error but still respond with a 200.
          log.error('Account.devicesNotify', {
            uid: uid,
            error: err
          });
        }
      }
    },
    {
      method: 'GET',
      path: '/account/devices',
      options: {
        auth: {
          strategies: [
            'sessionToken',
            'refreshToken'
          ]
        },
        response: {
          schema: isA.array().items(isA.object({
            id: DEVICES_SCHEMA.id.required(),
            isCurrentDevice: isA.boolean().required(),
            lastAccessTime: isA.number().min(0).required().allow(null),
            lastAccessTimeFormatted: isA.string().optional().allow(''),
            approximateLastAccessTime: isA.number().min(earliestSaneTimestamp).optional(),
            approximateLastAccessTimeFormatted: isA.string().optional().allow(''),
            location: DEVICES_SCHEMA.location,
            name: DEVICES_SCHEMA.nameResponse.allow('').required(),
            type: DEVICES_SCHEMA.type.required(),
            pushCallback: DEVICES_SCHEMA.pushCallback.allow(null).optional(),
            pushPublicKey: DEVICES_SCHEMA.pushPublicKey.allow(null).optional(),
            pushAuthKey: DEVICES_SCHEMA.pushAuthKey.allow(null).optional(),
            pushEndpointExpired: DEVICES_SCHEMA.pushEndpointExpired.optional(),
            availableCommands: DEVICES_SCHEMA.availableCommands.optional(),
          }).and('pushPublicKey', 'pushAuthKey'))
        }
      },
      handler: async function (request) {
        log.begin('Account.devices', request);

        const credentials = request.auth.credentials;

        return request.app.devices
          .then(deviceArray => {
            return deviceArray.map(device => {
              return Object.assign({
                id: device.id,
                isCurrentDevice: !! ((credentials.id && credentials.id === device.sessionToken) ||
                  (credentials.refreshTokenId && credentials.refreshTokenId === device.refreshTokenId)),
                location: marshallLocation(device.location, request),
                name: device.name || devices.synthesizeName(device),
                type: device.type || device.uaDeviceType || 'desktop',
                pushCallback: device.pushCallback,
                pushPublicKey: device.pushPublicKey,
                pushAuthKey: device.pushAuthKey,
                pushEndpointExpired: device.pushEndpointExpired,
                availableCommands: device.availableCommands
              }, marshallLastAccessTime(device.lastAccessTime, request));
            });
          }
        );
      }
    },
    {
      method: 'GET',
      path: '/account/sessions',
      options: {
        auth: {
          strategies: [
            'sessionToken',
            // this endpoint is only used by the content server
            // no refreshToken access here
          ]
        },
        response: {
          schema: isA.array().items(isA.object({
            id: isA.string().regex(HEX_STRING).required(),
            lastAccessTime: isA.number().min(0).required().allow(null),
            lastAccessTimeFormatted: isA.string().optional().allow(''),
            approximateLastAccessTime: isA.number().min(earliestSaneTimestamp).optional(),
            approximateLastAccessTimeFormatted: isA.string().optional().allow(''),
            createdTime: isA.number().min(0).required().allow(null),
            createdTimeFormatted: isA.string().optional().allow(''),
            location: DEVICES_SCHEMA.location,
            userAgent: isA.string().max(255).required().allow(''),
            os: isA.string().max(255).allow('').allow(null),
            deviceId: DEVICES_SCHEMA.id.allow(null).required(),
            deviceName: DEVICES_SCHEMA.nameResponse.allow('').allow(null).required(),
            deviceAvailableCommands: DEVICES_SCHEMA.availableCommands.allow(null).required(),
            deviceType: DEVICES_SCHEMA.type.allow(null).required(),
            deviceCallbackURL: DEVICES_SCHEMA.pushCallback.allow(null).required(),
            deviceCallbackPublicKey: DEVICES_SCHEMA.pushPublicKey.allow(null).required(),
            deviceCallbackAuthKey: DEVICES_SCHEMA.pushAuthKey.allow(null).required(),
            deviceCallbackIsExpired: DEVICES_SCHEMA.pushEndpointExpired.allow(null).required(),
            isDevice: isA.boolean().required(),
            isCurrentDevice: isA.boolean().required()
          }))
        }
      },
      handler: async function (request) {
        log.begin('Account.sessions', request);

        const sessionToken = request.auth.credentials;
        const uid = sessionToken.uid;

        return db.sessions(uid)
          .then(sessions => {
            return sessions.map(session => {
              const deviceId = session.deviceId;
              const isDevice = !! deviceId;

              let deviceName = session.deviceName;
              if (! deviceName) {
                deviceName = devices.synthesizeName(session);
              }

              let userAgent;
              if (! session.uaBrowser) {
                userAgent = '';
              } else if (! session.uaBrowserVersion) {
                userAgent = session.uaBrowser;
              } else {
                const { uaBrowser: browser, uaBrowserVersion: version } = session;
                userAgent = `${browser} ${version.split('.')[0]}`;
              }

              return Object.assign({
                deviceId,
                deviceName,
                deviceType: session.uaDeviceType || 'desktop',
                deviceAvailableCommands: session.deviceAvailableCommands || null,
                deviceCallbackURL: session.deviceCallbackURL,
                deviceCallbackPublicKey: session.deviceCallbackPublicKey,
                deviceCallbackAuthKey: session.deviceCallbackAuthKey,
                deviceCallbackIsExpired: !! session.deviceCallbackIsExpired,
                id: session.id,
                isCurrentDevice: session.id === sessionToken.id,
                isDevice,
                location: marshallLocation(session.location, request),
                createdTime: session.createdAt,
                createdTimeFormatted: localizeTimestamp.format(
                  session.createdAt,
                  request.headers['accept-language']
                ),
                os: session.uaOS,
                userAgent
              }, marshallLastAccessTime(session.lastAccessTime, request));
            });
          }
        );
      }
    },
    {
      method: 'POST',
      path: '/account/device/destroy',
      options: {
        auth: {
          strategies: [
            'sessionToken',
            'refreshToken'
          ]
        },
        validate: {
          payload: {
            id: DEVICES_SCHEMA.id.required()
          }
        },
        response: {
          schema: {}
        }
      },
      handler: async function (request) {
        log.begin('Account.deviceDestroy', request);

        const credentials = request.auth.credentials;
        const uid = credentials.uid;
        const id = request.payload.id;
        let devices;

        // We want to include the disconnected device in the list
        // of devices to notify, so list them before disconnecting.
        return request.app.devices
          .then(res => {
            devices = res;
            return db.deleteDevice(uid, id);
          })
          .then(() => {
            const deviceToDelete = devices.find(d => d.id === id);
            if (deviceToDelete && deviceToDelete.refreshTokenId) {
              // attempt to clean up the refreshToken in the OAuth DB
              return oauthdb.revokeRefreshTokenById(deviceToDelete.refreshTokenId).catch((err) => {
                log.error('deviceDestroy.revokeRefreshTokenById.error', {err: err.message});
              });
            }
          })
          .then(() => {
            push.notifyDeviceDisconnected(uid, devices, id)
              .catch(() => {});
            return P.all([
              request.emitMetricsEvent('device.deleted', {
                uid: uid,
                device_id: id
              }),
              log.notifyAttachedServices('device:delete', request, {
                uid: uid,
                id: id,
                timestamp: Date.now()
              })
            ]);
          })
          .then(() => { return {}; });
      }
    }
  ];
};
示例#9
0
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Joi = require("joi");
const posts = require("./post");
const comments = require("./comment");
const reactions = require("./reaction");
const user = require("./user");
const friends = require("./friend");
const commons = require("../utils/commons");
const postUrlSchema = Joi.object({
    user: commons.user.required().description('The post\'s author, identified as `username@instance-domain.tld`'),
    timestamp: Joi.number().integer().min(1).required().description('The post\'s creation timestamp')
}).label('PostParams');
const tokens = Joi.object({
    idToken: Joi.string().description('Identification token bound to a friend'),
    signature: Joi.string().when('idToken', { is: Joi.string().required(), then: Joi.required(), otherwise: Joi.optional() }).description('Request signature. Must be provided if an idToken is provided')
}).label('Tokens');
module.exports = {
    v1: {
        '/client/me': {
            get: {
                description: 'Retrieve data on the current user',
                notes: 'Retrieve data on the current user. Full documentation is available [here](https://github.com/JosephCaillet/vinimay/wiki/Client-to-server-API#retrieval).',
                handler: user.get,
                plugins: {
                    'hapi-swagger': {
                        responses: {
                            '200': {
                                description: 'Data on the current user',
                                schema: user.schema
                            }, '401': {
示例#10
0
/*global require, module, applicationContext */
'use strict';

var _ = require('underscore');
var joi = require('joi');
var actions = require('./actions');

module.exports = {
  mount: applicationContext.mount,
  name: 'segment',
  maxFailures: applicationContext.configuration.maxFailures,
  schema: joi.object().keys({
    0: _.reduce(actions, function (schema, actionSchema, actionName) {
      return schema.valid(actionName);
    }, joi.required()),
    1: _.reduce(actions, function (schema, actionSchema, actionName) {
      return schema.when('0', {is: actionName, then: actionSchema});
    }, joi.alternatives().required())
  }).unknown(true).required().meta({isTuple: true})
};
示例#11
0
文件: schema.js 项目: elastic/kibana
export default () => Joi.object({
  pkg: Joi.object({
    version: Joi.string().default(Joi.ref('$version')),
    branch: Joi.string().default(Joi.ref('$branch')),
    buildNum: Joi.number().default(Joi.ref('$buildNum')),
    buildSha: Joi.string().default(Joi.ref('$buildSha')),
  }).default(),

  env: Joi.object({
    name: Joi.string().default(Joi.ref('$env')),
    dev: Joi.boolean().default(Joi.ref('$dev')),
    prod: Joi.boolean().default(Joi.ref('$prod'))
  }).default(),

  dev: Joi.object({
    basePathProxyTarget: Joi.number().default(5603),
  }).default(),

  pid: Joi.object({
    file: Joi.string(),
    exclusive: Joi.boolean().default(false)
  }).default(),

  csp: Joi.object({
    rules: Joi.array().items(Joi.string()).default(DEFAULT_CSP_RULES),
    strict: Joi.boolean().default(false),
    warnLegacyBrowsers: Joi.boolean().default(true),
  }).default(),

  cpu: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  cpuacct: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  server: Joi.object({
    uuid: Joi.string().guid().default(),
    name: Joi.string().default(os.hostname()),
    host: Joi.string().hostname().default('localhost'),
    port: Joi.number().default(5601),
    maxPayloadBytes: Joi.number().default(1048576),
    autoListen: Joi.boolean().default(true),
    defaultRoute: Joi.string().default('/app/kibana').regex(/^\//, `start with a slash`),
    basePath: Joi.string().default('').allow('').regex(/(^$|^\/.*[^\/]$)/, `start with a slash, don't end with one`),
    rewriteBasePath: Joi.boolean().when('basePath', {
      is: '',
      then: Joi.default(false).valid(false),
      otherwise: Joi.default(false),
    }),
    customResponseHeaders: Joi.object().unknown(true).default({}),
    ssl: Joi.object({
      enabled: Joi.boolean().default(false),
      redirectHttpFromPort: Joi.number(),
      certificate: Joi.string().when('enabled', {
        is: true,
        then: Joi.required(),
      }),
      key: Joi.string().when('enabled', {
        is: true,
        then: Joi.required()
      }),
      keyPassphrase: Joi.string(),
      certificateAuthorities: Joi.array().single().items(Joi.string()).default([]),
      supportedProtocols: Joi.array().items(Joi.string().valid('TLSv1', 'TLSv1.1', 'TLSv1.2')).default(['TLSv1.1', 'TLSv1.2']),
      cipherSuites: Joi.array().items(Joi.string()).default(cryptoConstants.defaultCoreCipherList.split(':'))
    }).default(),
    cors: Joi.when('$dev', {
      is: true,
      then: Joi.object().default({
        origin: ['*://localhost:9876'] // karma test server
      }),
      otherwise: Joi.boolean().default(false)
    }),
    xsrf: Joi.object({
      disableProtection: Joi.boolean().default(false),
      whitelist: Joi.array().items(
        Joi.string().regex(/^\//, 'start with a slash')
      ).default([]),
      token: Joi.string().optional().notes('Deprecated')
    }).default(),
  }).default(),

  uiSettings: Joi.object().keys({
    overrides: Joi.object().unknown(true).default()
  }).default(),

  logging: Joi.object().keys({
    silent: Joi.boolean().default(false),

    quiet: Joi.boolean()
      .when('silent', {
        is: true,
        then: Joi.default(true).valid(true),
        otherwise: Joi.default(false)
      }),

    verbose: Joi.boolean()
      .when('quiet', {
        is: true,
        then: Joi.valid(false).default(false),
        otherwise: Joi.default(false)
      }),
    events: Joi.any().default({}),
    dest: Joi.string().default('stdout'),
    filter: Joi.any().default({}),
    json: Joi.boolean()
      .when('dest', {
        is: 'stdout',
        then: Joi.default(!process.stdout.isTTY),
        otherwise: Joi.default(true)
      }),
    timezone: Joi.string()
  }).default(),

  ops: Joi.object({
    interval: Joi.number().default(5000),
  }).default(),

  plugins: Joi.object({
    paths: Joi.array().items(Joi.string()).default([]),
    scanDirs: Joi.array().items(Joi.string()).default([]),
    initialize: Joi.boolean().default(true)
  }).default(),

  path: Joi.object({
    data: Joi.string().default(getData())
  }).default(),

  migrations: Joi.object({
    batchSize: Joi.number().default(100),
    scrollDuration: Joi.string().default('15m'),
    pollInterval: Joi.number().default(1500),
  }).default(),

  stats: Joi.object({
    maximumWaitTimeForAllCollectorsInS: Joi.number().default(60)
  }).default(),

  optimize: Joi.object({
    enabled: Joi.boolean().default(true),
    bundleFilter: Joi.string().default('!tests'),
    bundleDir: Joi.string().default(fromRoot('optimize/bundles')),
    viewCaching: Joi.boolean().default(Joi.ref('$prod')),
    watch: Joi.boolean().default(false),
    watchPort: Joi.number().default(5602),
    watchHost: Joi.string().hostname().default('localhost'),
    watchPrebuild: Joi.boolean().default(false),
    watchProxyTimeout: Joi.number().default(5 * 60000),
    useBundleCache: Joi.boolean().default(Joi.ref('$prod')),
    sourceMaps: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.string().required(),
          Joi.boolean()
        )
        .default('#cheap-source-map'),
    }),
    workers: Joi.number().min(1),
    profile: Joi.boolean().default(false)
  }).default(),
  status: Joi.object({
    allowAnonymous: Joi.boolean().default(false)
  }).default(),
  map: Joi.object({
    includeElasticMapsService: Joi.boolean().default(true),
    proxyElasticMapsServiceInMaps: Joi.boolean().default(false),
    tilemap: Joi.object({
      url: Joi.string(),
      options: Joi.object({
        attribution: Joi.string(),
        minZoom: Joi.number().min(0, 'Must be 0 or higher').default(0),
        maxZoom: Joi.number().default(10),
        tileSize: Joi.number(),
        subdomains: Joi.array().items(Joi.string()).single(),
        errorTileUrl: Joi.string().uri(),
        tms: Joi.boolean(),
        reuseTiles: Joi.boolean(),
        bounds: Joi.array().items(Joi.array().items(Joi.number()).min(2).required()).min(2),
        default: Joi.boolean()
      }).default({
        default: true
      })
    }).default(),
    regionmap: Joi.object({
      includeElasticMapsService: Joi.boolean().default(true),
      layers: Joi.array().items(Joi.object({
        url: Joi.string(),
        format: Joi.object({
          type: Joi.string().default('geojson')
        }).default({
          type: 'geojson'
        }),
        meta: Joi.object({
          feature_collection_path: Joi.string().default('data')
        }).default({
          feature_collection_path: 'data'
        }),
        attribution: Joi.string(),
        name: Joi.string(),
        fields: Joi.array().items(Joi.object({
          name: Joi.string(),
          description: Joi.string()
        }))
      })).default([])
    }).default(),
    manifestServiceUrl: Joi.string().default('https://catalogue-staging.maps.elastic.co/v7.2/manifest'),
    emsLandingPageUrl: Joi.string().default('https://maps.elastic.co/v7.0'),
    emsTileLayerId: Joi.object({
      bright: Joi.string().default('road_map'),
      desaturated: Joi.string().default('road_map_desaturated'),
      dark: Joi.string().default('dark_map'),
    }).default({
      bright: 'road_map',
      desaturated: 'road_map_desaturated',
      dark: 'dark_map',
    }),
  }).default(),

  i18n: Joi.object({
    locale: Joi.string().default('en'),
  }).default(),

  savedObjects: Joi.object({
    maxImportPayloadBytes: Joi.number().default(10485760),
    maxImportExportSize: Joi.number().default(10000),
  }).default(),

}).default();
示例#12
0
var _ = require('lodash');
var Joi = require('joi');

var common = {
  team: Joi.number().integer().min(0),
  userUuid: Joi.string().regex(/^[A-Za-z0-9_-]+$/).min(1, 'utf8').max(128, 'utf8'),
  primaryKeyId: Joi.number().integer().min(0)
};

const schemas = {
  common: common,

  action: {
    user: common.userUuid.required(),
    type: Joi.string().uppercase().required(),
    imageData: Joi.string().when('type', { is: 'IMAGE', then: Joi.required() }),
    text: Joi.string().when('type', { is: 'TEXT', then: Joi.required() }),
    location: Joi.object({
      latitude: Joi.number(),
      longitude: Joi.number()
    })
  },

  user: {
    uuid: common.userUuid.required(),
    name: Joi.string().min(1, 'utf8').max(50, 'utf8').required(),
    team: common.team.required()
  },

  feedParams: {
    beforeId: Joi.number().integer().min(0).optional(),
示例#13
0
const HABITAT_PACKAGE = Joi
    .string()
    .description('Package of the Habitat command')
    .example('core/git/2.14.1');

const HABITAT_COMMAND = Joi
    .string()
    .description('Executable of the Habitat command')
    .example('git');

const SCHEMA_HABITAT = Joi.object()
    .keys({
        mode: HABITAT_MODE,
        file: HABITAT_FILE
            .when('mode', { is: 'local', then: Joi.required() }),
        package: HABITAT_PACKAGE,
        command: HABITAT_COMMAND
    })
    .requiredKeys('mode', 'package', 'command');

const DOCKER_IMAGE = Joi
    .string()
    .description('Image of the Docker command')
    .example('chefdk:1.2.3');

const DOCKER_COMMAND = Joi
    .string()
    .description('Executable of the Docker command')
    .default('')
    .example('knife');
示例#14
0
'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Call = require('call');
var Hoek = require('hoek');
var Joi = require('joi');

var Handler = Joi.alternatives([Joi.func(), Joi.object({
  statusCode: Joi.number().positive().integer().min(200).default(200),
  headers: Joi.object(),
  payload: Joi.required()
}), Joi.object({
  isBoom: true
}).unknown()]);

var _route = Joi.alternatives([Joi.object({
  method: Joi.string().regex(/^[a-zA-Z0-9!#\$%&'\*\+\-\.^_`\|~]+$/).lowercase().default('get'),
  path: Joi.string().required(),
  handler: Handler,
  vhost: Joi.array().items(Joi.string().hostname()).min(1).single().default(['*']),
  filter: Joi.func().optional()
}), Joi.object({
  method: Joi.string().regex(/^[a-zA-Z0-9!#\$%&'\*\+\-\.^_`\|~]+$/).lowercase().default('get'),
  path: Joi.string().required(),
  config: Joi.object({
    handler: Handler
  }).unknown(),
  vhost: Joi.array().items(Joi.string().hostname()).min(1).single().default(['*']),
示例#15
0
 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/
var Joi = require('joi');

var blueprintValidator = module.exports = {};

blueprintValidator.launch = {
    params: {
        blueprintId: Joi.required()
    },
    body: {
        version: Joi.required(),
        envId: Joi.required()
    }
};

blueprintValidator.create = {
    body: {
        "name": Joi.string().required(),
        "organizationId": Joi.string().required(),
        "businessGroupId": Joi.string().required(),
        "projectId": Joi.string().required(),
        "networkProfileId": Joi.string().required(),
        "vmImageId": Joi.string().required(),
示例#16
0
exports.register = function (server, options, next) {

    server.auth.scheme('cookie', internals.implementation);
    next();
};


exports.register.attributes = {
    pkg: require('../package.json')
};

internals.schema = Joi.object({
    cookie: Joi.string().default('sid'),
    password: Joi.alternatives(Joi.string(), Joi.object().type(Buffer)).required(),
    ttl: Joi.number().integer().min(0).when('keepAlive', { is: true, then: Joi.required() }),
    domain: Joi.string().allow(null),
    path: Joi.string().default('/'),
    clearInvalid: Joi.boolean().default(false),
    keepAlive: Joi.boolean().default(false),
    isSecure: Joi.boolean().default(true),
    isHttpOnly: Joi.boolean().default(true),
    redirectTo: Joi.string().allow(false),
    appendNext: Joi.alternatives(Joi.string(), Joi.boolean()).default(false),
    redirectOnTry: Joi.boolean().default(true),
    validateFunc: Joi.func(),
    requestDecoratorName: Joi.string().default('cookieAuth')
}).required();

internals.implementation = function (server, options) {
示例#17
0
var Joi = require('joi');
import CONST from '../constants';

var common = {
  team: Joi.number().integer().min(0),
  userUuid: Joi.string().regex(/^[A-Za-z0-9_-]+$/).min(1, 'utf8').max(128, 'utf8'),
  primaryKeyId: Joi.number().integer().min(0)
};

const schemas = {
  common: common,

  action: {
    user: common.userUuid.required(),
    type: Joi.string().uppercase().required(),
    imageData: Joi.string().when('type', { is: 'IMAGE', then: Joi.required() }),
    imageText: Joi.string().max(50, 'utf8').optional(),
    imageTextPosition: Joi.number().min(0).max(1).optional(),
    text: Joi.string().when('type', { is: 'TEXT', then: Joi.required() }),
    eventId: common.primaryKeyId.when('type', { is: 'CHECK_IN_EVENT', then: Joi.required()}),
    city: common.primaryKeyId,
    location: Joi.object({
      latitude: Joi.number(),
      longitude: Joi.number()
    }).when('type', {is: 'CHECK_IN_EVENT', then: Joi.required()}),
  },

  user: {
    uuid: common.userUuid.required(),
    name: Joi.string().min(1, 'utf8').max(50, 'utf8').required(),
    team: common.team.required()
示例#18
0
文件: index.js 项目: ldesplat/bell
    server.auth.scheme('bell', internals.implementation);
    server.expose('oauth', OAuth);
    next();
};

exports.register.attributes = {
    pkg: require('../package.json')
};


internals.schema = Joi.object({
    provider: Joi.object({
        name: Joi.string().optional().default('custom'),
        protocol: Joi.string().valid('oauth', 'oauth2'),
        temporary: Joi.string().when('protocol', { is: 'oauth', then: Joi.required(), otherwise: Joi.forbidden() }),
        signatureMethod: Joi.string().valid('HMAC-SHA1', 'RSA-SHA1').when('protocol', { is: 'oauth', then: Joi.default('HMAC-SHA1'), otherwise: Joi.forbidden() }),
        auth: Joi.string().required(),
        useParamsAuth: Joi.boolean().default(false).when('protocol', { is: 'oauth2', then: Joi.optional(), otherwise: Joi.forbidden() }),
        token: Joi.string().required(),
        headers: Joi.object(),
        profile: Joi.func(),
        scope: Joi.alternatives().try(
            Joi.array().items(Joi.string()),
            Joi.func()
        ).when('protocol', { is: 'oauth2', otherwise: Joi.forbidden() }),
        scopeSeparator: Joi.string().when('protocol', { is: 'oauth2', otherwise: Joi.forbidden() }),
        version: Joi.string()
    }).required(),
    password: Joi.string().required(),
    clientId: Joi.string().required(),
示例#19
0
文件: team.js 项目: dfrsol/newww
      if (err.statusCode === 404) {
        return reply.view('errors/not-found', err).code(404);
      } else if (err.statusCode < 500) {
        return handleUserError(request, reply, '/org/' + orgName, err.message);
      } else {
        return reply.view('errors/internal', err);
      }
    });
};

var validPayloadSchema = {
  updateType: Joi.string().required(),
  name: Joi.string().when('updateType', {
    is: 'updateWritePermissions',
    then: Joi.required()
  }).when('updateType', {
    is: 'removePackage',
    then: Joi.required()
  }).when('updateType', {
    is: 'removeUser',
    then: Joi.required()
  }),
  names: Joi.any().when('updateType', {
    is: 'addPackagesToTeam',
    then: Joi.required()
  }),
  writePermission: Joi.string().when('updateType', {
    is: 'updateWritePermissions',
    then: Joi.allow('on')
  }),
示例#20
0
文件: route.js 项目: geek/hapi
    it('ignores validation on * route when request is GET', async () => {

        const server = Hapi.server();
        server.route({ method: '*', path: '/', handler: () => null, options: { validate: { payload: { a: Joi.required() } } } });
        const res = await server.inject('/');
        expect(res.statusCode).to.equal(200);
    });
示例#21
0
export default () => Joi.object({
  pkg: Joi.object({
    version: Joi.string().default(Joi.ref('$version')),
    branch: Joi.string().default(Joi.ref('$branch')),
    buildNum: Joi.number().default(Joi.ref('$buildNum')),
    buildSha: Joi.string().default(Joi.ref('$buildSha')),
  }).default(),

  env: Joi.object({
    name: Joi.string().default(Joi.ref('$env')),
    dev: Joi.boolean().default(Joi.ref('$dev')),
    prod: Joi.boolean().default(Joi.ref('$prod'))
  }).default(),

  dev: Joi.object({
    basePathProxyTarget: Joi.number().default(5603),
  }).default(),

  pid: Joi.object({
    file: Joi.string(),
    exclusive: Joi.boolean().default(false)
  }).default(),

  cpu: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  cpuacct: Joi.object({
    cgroup: Joi.object({
      path: Joi.object({
        override: Joi.string().default()
      })
    })
  }),

  server: Joi.object({
    uuid: Joi.string().guid().default(),
    name: Joi.string().default(os.hostname()),
    host: Joi.string().hostname().default('localhost'),
    port: Joi.number().default(5601),
    maxPayloadBytes: Joi.number().default(1048576),
    autoListen: Joi.boolean().default(true),
    defaultRoute: Joi.string().default('/app/kibana').regex(/^\//, `start with a slash`),
    basePath: Joi.string().default('').allow('').regex(/(^$|^\/.*[^\/]$)/, `start with a slash, don't end with one`),
    customResponseHeaders: Joi.object().unknown(true).default({}),
    ssl: Joi.object({
      enabled: Joi.boolean().default(false),
      redirectHttpFromPort: Joi.number(),
      certificate: Joi.string().when('enabled', {
        is: true,
        then: Joi.required(),
      }),
      key: Joi.string().when('enabled', {
        is: true,
        then: Joi.required()
      }),
      keyPassphrase: Joi.string(),
      certificateAuthorities: Joi.array().single().items(Joi.string()),
      supportedProtocols: Joi.array().items(Joi.string().valid('TLSv1', 'TLSv1.1', 'TLSv1.2')),
      cipherSuites: Joi.array().items(Joi.string()).default(cryptoConstants.defaultCoreCipherList.split(':'))
    }).default(),
    cors: Joi.when('$dev', {
      is: true,
      then: Joi.object().default({
        origin: ['*://localhost:9876'] // karma test server
      }),
      otherwise: Joi.boolean().default(false)
    }),
    xsrf: Joi.object({
      disableProtection: Joi.boolean().default(false),
      token: Joi.string().optional().notes('Deprecated')
    }).default(),
  }).default(),

  logging: Joi.object().keys({
    silent: Joi.boolean().default(false),

    quiet: Joi.boolean()
      .when('silent', {
        is: true,
        then: Joi.default(true).valid(true),
        otherwise: Joi.default(false)
      }),

    verbose: Joi.boolean()
      .when('quiet', {
        is: true,
        then: Joi.valid(false).default(false),
        otherwise: Joi.default(false)
      }),

    events: Joi.any().default({}),
    dest: Joi.string().default('stdout'),
    filter: Joi.any().default({}),
    json: Joi.boolean()
      .when('dest', {
        is: 'stdout',
        then: Joi.default(!process.stdout.isTTY),
        otherwise: Joi.default(true)
      }),

    useUTC: Joi.boolean().default(true),
  })
    .default(),

  ops: Joi.object({
    interval: Joi.number().default(5000),
  }).default(),

  plugins: Joi.object({
    paths: Joi.array().items(Joi.string()).default([]),
    scanDirs: Joi.array().items(Joi.string()).default([]),
    initialize: Joi.boolean().default(true)
  }).default(),

  path: Joi.object({
    data: Joi.string().default(getData())
  }).default(),

  optimize: Joi.object({
    enabled: Joi.boolean().default(true),
    bundleFilter: Joi.string().default('!tests'),
    bundleDir: Joi.string().default(fromRoot('optimize/bundles')),
    viewCaching: Joi.boolean().default(Joi.ref('$prod')),
    watch: Joi.boolean().default(false),
    watchPort: Joi.number().default(5602),
    watchHost: Joi.string().hostname().default('localhost'),
    watchPrebuild: Joi.boolean().default(false),
    watchProxyTimeout: Joi.number().default(5 * 60000),
    useBundleCache: Joi.boolean().default(Joi.ref('$prod')),
    unsafeCache: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.boolean(),
          Joi.string().regex(/^\/.+\/$/)
        )
        .default(true),
    }),
    sourceMaps: Joi.when('$prod', {
      is: true,
      then: Joi.boolean().valid(false),
      otherwise: Joi
        .alternatives()
        .try(
          Joi.string().required(),
          Joi.boolean()
        )
        .default('#cheap-source-map'),
    }),
    profile: Joi.boolean().default(false)
  }).default(),
  status: Joi.object({
    allowAnonymous: Joi.boolean().default(false)
  }).default(),
  map: Joi.object({
    manifestServiceUrl: Joi.when('$dev', {
      is: true,
      then: Joi.string().default('https://staging-dot-catalogue-dot-elastic-layer.appspot.com/v1/manifest'),
      otherwise: Joi.string().default('https://catalogue.maps.elastic.co/v1/manifest')
    })
  }).default(),
  tilemap: Joi.object({
    url: Joi.string(),
    options: Joi.object({
      attribution: Joi.string(),
      minZoom: Joi.number().min(0, 'Must be 0 or higher').default(0),
      maxZoom: Joi.number().default(10),
      tileSize: Joi.number(),
      subdomains: Joi.array().items(Joi.string()).single(),
      errorTileUrl: Joi.string().uri(),
      tms: Joi.boolean(),
      reuseTiles: Joi.boolean(),
      bounds: Joi.array().items(Joi.array().items(Joi.number()).min(2).required()).min(2)
    }).default()
  }).default(),
  regionmap: Joi.object({
    includeElasticMapsService: Joi.boolean().default(true),
    layers: Joi.array().items(Joi.object({
      url: Joi.string(),
      type: Joi.string(),
      attribution: Joi.string(),
      name: Joi.string(),
      fields: Joi.array().items(Joi.object({
        name: Joi.string(),
        description: Joi.string()
      }))
    }))
  }).default(),

  i18n: Joi.object({
    defaultLocale: Joi.string().default('en'),
  }).default(),

}).default();
    file:///usr/local/lib/node_modules/makemehapi/node_modules/joi/README.md
*/

var server = new (require('hapi').Server)()
var vision = require('vision')
var joi = require('joi')

server.connection({
  host: 'localhost'
  , port: Number(process.argv[2] || 8080)
})

server.route({
  path: '/chickens/{breed}'
  , method:'GET'
  , handler: function(request, reply) {
    // no-op
  }
  , config: {
    validate: {
      params: {
        breed: joi.required()
      }
    }
  }
})

server.start(function() {
  console.log('Server running at:', server.info.uri)
})
示例#23
0
    it('ignores validation on * route when request is GET', function (done) {

        var server = new Hapi.Server();
        server.connection();
        server.route({ method: '*', path: '/', handler: function (request, reply) { return reply(); }, config: { validate: { payload: { a: Joi.required() } } } });
        server.inject('/', function (res) {

            expect(res.statusCode).to.equal(200);
            done();
        });
    });
示例#24
0
文件: schema.js 项目: plaidev/vogels
'use strict';

var Joi      = require('joi'),
    nodeUUID = require('node-uuid'),
    _        = require('lodash');

var internals =  {};

internals.secondaryIndexSchema = Joi.object().keys({
  hashKey : Joi.string().when('type', { is: 'local', then: Joi.ref('$hashKey'), otherwise : Joi.required()}),
  rangeKey: Joi.string().when('type', { is: 'local', then: Joi.required(), otherwise: Joi.optional() }),
  type : Joi.string().valid('local', 'global').required(),
  name : Joi.string().required(),
  projection : Joi.object(),
  readCapacity : Joi.number().when('type', { is: 'global', then: Joi.optional(), otherwise : Joi.forbidden()}),
  writeCapacity : Joi.number().when('type', { is: 'global', then: Joi.optional(), otherwise : Joi.forbidden()})
});

internals.configSchema = Joi.object().keys({
  hashKey   : Joi.string().required(),
  rangeKey  : Joi.string(),
  tableName : Joi.alternatives().try(Joi.string(), Joi.func()),
  indexes   : Joi.array().includes(internals.secondaryIndexSchema),
  schema    : Joi.object(),
  timestamps : Joi.boolean().default(false)
}).required();

internals.wireType = function (key) {
  switch (key) {
    case 'string':
      return 'S';
},
    {
        method: 'GET',
        path: '/json',
        handler: function (request, reply) {
            reply({ hello: 'world' });
        }
    },
    {
        method: 'POST',
        path: '/login',
        config: {
            handler: function (request, reply) {
                reply('login successful');
            },
            validate: {
                payload: Joi.object({
                    isGuest: Joi.boolean().required(),
                    username: Joi.string().when('isGuest', { is: false, then: Joi.required() }),
                    password: Joi.string().alphanum(),
                    accessToken: Joi.string().alphanum()
                }).options({ allowUnknown: true }).without('password', 'accessToken')
            }
        }
    }

]);



示例#26
0
});


internals.plugin = internals.register.keys({
    register: Joi.func().keys({
        attributes: Joi.object({
            pkg: Joi.object({
                name: Joi.string(),
                version: Joi.string().default('0.0.0')
            })
                .unknown()
                .default({
                    version: '0.0.0'
                }),
            name: Joi.string()
                .when('pkg.name', { is: Joi.exist(), otherwise: Joi.required() }),
            version: Joi.string(),
            multiple: Joi.boolean().default(false),
            dependencies: Joi.array().items(Joi.string()).single(),
            connections: Joi.boolean().allow('conditional').default(true),
            once: Joi.boolean().valid(true)
        })
            .required()
            .unknown()
    })
        .required(),
    options: Joi.any()
})
    .without('once', 'options')
    .unknown();
示例#27
0
(async () => {
    try {
        const server = Hapi.Server({
            host: 'localhost',
            port: Number(process.argv[2] || 8080)
        });

        server.route({
            method: 'POST',
            path: '/login',
            config: {
                handler: (request, h) => {
                    return 'login successful';
                },
                validate: {
                    payload: Joi.object({
                        isGuest: Joi.boolean().required(),
                        username: Joi.string().when('isGuest', { is: false, then: Joi.required() }),
                        password: Joi.string().alphanum(),
                        accessToken: Joi.string().alphanum()
                    }).options({ allowUnknown: true }).without('password', 'accessToken')
                }
            }
        });

        await server.start();

    } catch (error) {
        console.log(error);
    }
})();
示例#28
0
import Joi from 'joi';
import jwt from 'jsonwebtoken';
import { decorators } from 'octobus.js';

const { withSchema } = decorators;

const schema = Joi.object().keys({
  id: Joi.required(),
  username: Joi.string().required(),
  options: Joi.object().default({}),
}).unknown(true).required();

export default ({ key, options: defaultOptions }) =>
  withSchema(schema)(
    async ({ params }) => {
      const { options, ...user } = params;
      return jwt.sign(user, key, { ...defaultOptions, ...options });
    },
  );