<commit_before>module.exports = {
  'rules': {
    // enforce spacing inside array brackets
    'array-bracket-spacing': [2, 'never'],
    // enforce one true brace style
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    // require camel case names
    'camelcase': [2, { 'properties': 'never' }],
    // enforce spacing before and after comma
    'comma-spacing': [2, { 'before': false, 'after': true }],
    // enforce one true comma style
    'comma-style': [2, 'last'],
    // disallow padding inside computed properties
    'computed-property-spacing': [2, 'never'],
    // enforces consistent naming when capturing the current execution context
    'consistent-this': 0,
    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 2,
    // require function expressions to have a name
    'func-names': 1,
    // enforces use of function declarations or expressions
    'func-style': 0,
    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 0,
    // this option sets a specific tab width for your code
    // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
    'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [2, 'prefer-double'],
    // enforces spacing between keys and values in object literal properties
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    // enforces empty lines around comments
    'lines-around-comment': 0,
    // disallow mixed 'LF' and 'CRLF' as linebreaks
    'linebreak-style': 0,
    // specify the maximum length of a line in your program
    // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md
    'max-len': [2, 100, 2, {
      'ignoreUrls': true,
      'ignoreComments': false
    }],
    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 0,
    // require a capital letter for constructors
    'new-cap': [2, { 'newIsCap': true }],
    // disallow the omission of parentheses when invoking a constructor with no arguments
    'new-parens': 0,
    // allow/disallow an empty newline after var statement
    'newline-after-var': 0,
    // disallow use of the Array constructor
    'no-array-constructor': 0,
    // disallow use of the continue statement
    'no-continue': 0,
    // disallow comments inline after code
    'no-inline-comments': 0,
    // disallow if as the only statement in an else block
    'no-lonely-if': 0,
    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 2,
    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
    // disallow nested ternary expressions
    'no-nested-ternary': 2,
    // disallow use of the Object constructor
    'no-new-object': 2,
    // disallow space between function identifier and application
    'no-spaced-func': 2,
    // disallow the use of ternary operators
    'no-ternary': 0,
    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 2,
    // disallow dangling underscores in identifiers
    'no-underscore-dangle': 0,
    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
    // require padding inside curly braces
    'object-curly-spacing': [2, 'always'],
    // allow just one var statement per function
    'one-var': [2, 'never'],
    // require assignment operator shorthand where possible or prohibit it entirely
    'operator-assignment': 0,
    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 0,
    // enforce padding within blocks
    'padded-blocks': [2, 'never'],
    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
    // specify whether double or single quotes should be used
    'quotes': [2, 'single', 'avoid-escape'],
    // require identifiers to match the provided regular expression
    'id-match': 0,
    // enforce spacing before and after semicolons
    'semi-spacing': [2, { 'before': false, 'after': true }],
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'always'],
    // sort variables within the same declaration block
    'sort-vars': 0,
    // require a space before & after certain keywords
    "keyword-spacing": [2, {"before": true, "after": true, "overrides": {
      'return': {'after': true},
      'throw': {'after': true},
      'case': {'after': true}
    }}],
    // require or disallow space before blocks
    'space-before-blocks': 2,
    // require or disallow space before function opening parenthesis
    // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
    'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
    // require or disallow spaces inside parentheses
    'space-in-parens': [2, 'never'],
    // require spaces around operators
    'space-infix-ops': 2,
    // Require or disallow spaces before/after unary operators
    'space-unary-ops': 0,
    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': [2, 'always', {
      'exceptions': ['-', '+'],
      'markers': ['=', '!']           // space here to support sprockets directives
    }],
    // require regex literals to be wrapped in parentheses
    'wrap-regex': 0
  }
};
<commit_msg>[eslint-v2] fix keyword-spacing style and sorting<commit_after>module.exports = {
  'rules': {
    // enforce spacing inside array brackets
    'array-bracket-spacing': [2, 'never'],
    // enforce one true brace style
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    // require camel case names
    'camelcase': [2, { 'properties': 'never' }],
    // enforce spacing before and after comma
    'comma-spacing': [2, { 'before': false, 'after': true }],
    // enforce one true comma style
    'comma-style': [2, 'last'],
    // disallow padding inside computed properties
    'computed-property-spacing': [2, 'never'],
    // enforces consistent naming when capturing the current execution context
    'consistent-this': 0,
    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 2,
    // require function expressions to have a name
    'func-names': 1,
    // enforces use of function declarations or expressions
    'func-style': 0,
    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 0,
    // this option sets a specific tab width for your code
    // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
    'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [2, 'prefer-double'],
    // enforces spacing between keys and values in object literal properties
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    // require a space before & after certain keywords
    'keyword-spacing': [2, {
      'before': true,
      'after': true,
      'overrides': {
        'return': { 'after': true },
        'throw': { 'after': true },
        'case': { 'after': true }
      }
    }],
    // enforces empty lines around comments
    'lines-around-comment': 0,
    // disallow mixed 'LF' and 'CRLF' as linebreaks
    'linebreak-style': 0,
    // specify the maximum length of a line in your program
    // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md
    'max-len': [2, 100, 2, {
      'ignoreUrls': true,
      'ignoreComments': false
    }],
    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 0,
    // require a capital letter for constructors
    'new-cap': [2, { 'newIsCap': true }],
    // disallow the omission of parentheses when invoking a constructor with no arguments
    'new-parens': 0,
    // allow/disallow an empty newline after var statement
    'newline-after-var': 0,
    // disallow use of the Array constructor
    'no-array-constructor': 0,
    // disallow use of the continue statement
    'no-continue': 0,
    // disallow comments inline after code
    'no-inline-comments': 0,
    // disallow if as the only statement in an else block
    'no-lonely-if': 0,
    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 2,
    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
    // disallow nested ternary expressions
    'no-nested-ternary': 2,
    // disallow use of the Object constructor
    'no-new-object': 2,
    // disallow space between function identifier and application
    'no-spaced-func': 2,
    // disallow the use of ternary operators
    'no-ternary': 0,
    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 2,
    // disallow dangling underscores in identifiers
    'no-underscore-dangle': 0,
    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
    // require padding inside curly braces
    'object-curly-spacing': [2, 'always'],
    // allow just one var statement per function
    'one-var': [2, 'never'],
    // require assignment operator shorthand where possible or prohibit it entirely
    'operator-assignment': 0,
    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 0,
    // enforce padding within blocks
    'padded-blocks': [2, 'never'],
    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
    // specify whether double or single quotes should be used
    'quotes': [2, 'single', 'avoid-escape'],
    // require identifiers to match the provided regular expression
    'id-match': 0,
    // enforce spacing before and after semicolons
    'semi-spacing': [2, { 'before': false, 'after': true }],
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'always'],
    // sort variables within the same declaration block
    'sort-vars': 0,
    // require or disallow space before blocks
    'space-before-blocks': 2,
    // require or disallow space before function opening parenthesis
    // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
    'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
    // require or disallow spaces inside parentheses
    'space-in-parens': [2, 'never'],
    // require spaces around operators
    'space-infix-ops': 2,
    // Require or disallow spaces before/after unary operators
    'space-unary-ops': 0,
    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': [2, 'always', {
      'exceptions': ['-', '+'],
      'markers': ['=', '!']           // space here to support sprockets directives
    }],
    // require regex literals to be wrapped in parentheses
    'wrap-regex': 0
  }
};
<|endoftext|>


<commit_before>'use strict'; // eslint-disable-line

const Logger = require('werelogs').Logger;
const errors = require('arsenal').errors;
const MongoClient = require('arsenal').storage
    .metadata.mongoclient.MongoClientInterface;

const BackbeatConsumer = require('../../lib/BackbeatConsumer');
const QueueEntry = require('../../lib/models/QueueEntry');
const DeleteOpQueueEntry = require('../../lib/models/DeleteOpQueueEntry');
const BucketQueueEntry = require('../../lib/models/BucketQueueEntry');
const BucketMdQueueEntry = require('../../lib/models/BucketMdQueueEntry');
const ObjectQueueEntry = require('../../lib/models/ObjectQueueEntry');

// TODO - ADD PREFIX BASED ON SOURCE
// april 6, 2018

/**
 * @class MongoQueueProcessor
 *
 * @classdesc Background task that processes entries from the
 * ingestion for kafka queue and pushes entries to mongo
 */
class MongoQueueProcessor {

    /**
     * @constructor
     * @param {Object} kafkaConfig - kafka configuration object
     * @param {String} kafkaConfig.hosts - list of kafka brokers
     *   as "host:port[,host:port...]"
     * @param {Object} mongoProcessorConfig - mongo processor configuration
     *   object
     * @param {String} mongoProcessorConfig.topic - topic name
     * @param {String} mongoProcessorConfig.groupId - kafka
     *   consumer group ID
     * @param {number} [mongoProcessorConfig.retry.timeoutS] -
     *  retry timeout in secs.
     * @param {number} [mongoProcessorConfig.retry.maxRetries] -
     *  max retries before giving up
     * @param {Object} [mongoProcessorConfig.retry.backoff] -
     *  backoff params
     * @param {number} [mongoProcessorConfig.retry.backoff.min] -
     *  min. backoff in ms.
     * @param {number} [mongoProcessorConfig.retry.backoff.max] -
     *  max. backoff in ms.
     * @param {number} [mongoProcessorConfig.retry.backoff.jitter] -
     *  randomness
     * @param {number} [mongoProcessorConfig.retry.backoff.factor] -
     *  backoff factor
     * @param {Object} mongoClientConfig - config for connecting to mongo
     * @param {String} site - site name
     */
    constructor(kafkaConfig, mongoProcessorConfig, mongoClientConfig, site) {
        this.kafkaConfig = kafkaConfig;
        this.mongoProcessorConfig = mongoProcessorConfig;
        this.mongoClientConfig = mongoClientConfig;
        this.site = site;

        this._consumer = null;
        this.logger =
            new Logger(`Backbeat:Ingestion:MongoProcessor:${this.site}`);
        this.mongoClientConfig.logger = this.logger;
        this._mongoClient = new MongoClient(this.mongoClientConfig);
    }

    /**
     * Start kafka consumer
     *
     * @return {undefined}
     */
    start() {
        this.logger.info('starting mongo queue processor');
        this._mongoClient.setup(err => {
            if (err) {
                this.logger.error('could not connect to MongoDB', { err });
                process.exit(1);
            }
            let consumerReady = false;
            this._consumer = new BackbeatConsumer({
                topic: this.mongoProcessorConfig.topic,
                groupId: `${this.mongoProcessorConfig.groupId}-${this.site}`,
                kafka: { hosts: this.kafkaConfig.hosts },
                queueProcessor: this.processKafkaEntry.bind(this),
            });
            this._consumer.on('error', () => {
                if (!consumerReady) {
                    this.logger.fatal('error starting mongo queue processor');
                    process.exit(1);
                }
            });
            this._consumer.on('ready', () => {
                consumerReady = true;
                this._consumer.subscribe();
                this.logger.info('mongo queue processor is ready');
            });
        });
    }

    /**
     * Stop kafka consumer and commit current offset
     *
     * @param {function} done - callback
     * @return {undefined}
     */
    stop(done) {
        if (!this._consumer) {
            return setImmediate(done);
        }
        return this._consumer.close(done);
    }

    /**
     * Update ingested entry metadata fields: owner-id, owner-display-name
     * @param {ObjectQueueEntry} entry - object queue entry object
     * @param {object} ownerInfo - owner details
     * @return {undefined}
     */
    _updateOwnerMD(entry, ownerInfo) {
        // zenko bucket owner information is being set on ingested md
        entry.setOwnerDisplayName(ownerInfo.ownerDisplayName);
        entry.setOwnerId(ownerInfo.ownerId);
    }

    /**
     * Process a delete object entry
     * @param {DeleteOpQueueEntry} sourceEntry - delete object entry
     * @param {function} done - callback(error)
     * @return {undefined}
     */
    _processDeleteOpQueueEntry(sourceEntry, done) {
        const bucket = sourceEntry.getBucket();
        const key = sourceEntry.getObjectVersionedKey();

        // Always call deleteObject with version params undefined so
        // that mongoClient will use deleteObjectNoVer which just deletes
        // the object without further manipulation/actions.
        // S3 takes care of the versioning logic so consuming the queue
        // is sufficient to replay the version logic in the consumer.
        return this._mongoClient.deleteObject(bucket, key, undefined,
            this.logger, err => {
                if (err) {
                    this.logger.error('error deleting object metadata ' +
                    'from mongo', { bucket, key, error: err.message });
                    return done(err);
                }
                this.logger.info('object metadata deleted from mongo',
                { bucket, key });
                return done();
            });
    }

    /**
     * Process an object entry
     * @param {ObjectQueueEntry} sourceEntry - object metadata entry
     * @param {object} ownerInfo - owner details
     * @param {function} done - callback(error)
     * @return {undefined}
     */
    _processObjectQueueEntry(sourceEntry, ownerInfo, done) {
        const bucket = sourceEntry.getBucket();
        // always use versioned key so putting full version state to mongo
        const key = sourceEntry.getObjectVersionedKey();

        // update necessary metadata fields before saving to Zenko MongoDB
        this._updateOwnerMD(sourceEntry);

        const objVal = sourceEntry.getValue();
        // Always call putObject with version params undefined so
        // that mongoClient will use putObjectNoVer which just puts
        // the object without further manipulation/actions.
        // S3 takes care of the versioning logic so consuming the queue
        // is sufficient to replay the version logic in the consumer.
        return this._mongoClient.putObject(bucket, key, objVal, undefined,
            this.logger, err => {
                if (err) {
                    this.logger.error('error putting object metadata ' +
                    'to mongo', { error: err });
                    return done(err);
                }
                this.logger.info('object metadata put to mongo',
                { key });
                return done();
            });
    }

    /**
     * Put kafka queue entry into mongo
     *
     * @param {object} kafkaEntry - entry generated by ingestion populator
     * @param {string} kafkaEntry.key - kafka entry key
     * @param {string} kafkaEntry.value - kafka entry value
     * @param {function} done - callback function
     * @return {undefined}
     */
    processKafkaEntry(kafkaEntry, done) {
        const sourceEntry = QueueEntry.createFromKafkaEntry(kafkaEntry);
        if (sourceEntry.error) {
            this.logger.error('error processing source entry',
                              { error: sourceEntry.error });
            return process.nextTick(() => done(errors.InternalError));
        }
        // TODO-FIX:
        // Depends on the filter data. Need a way of determining the
        // zenko bucket.
        // if entry is for another site, simply skip/ignore
        if (this.site !== kafkaEntry.bucket) {
            return process.nextTick(done);
        }
        if (sourceEntry instanceof BucketMdQueueEntry) {
            this.logger.warn('skipping bucket md queue entry', {
                method: 'MongoQueueProcessor.processKafkaEntry',
                entry: sourceEntry.getLogInfo(),
            });
            return process.nextTick(done);
        } else if (sourceEntry instanceof BucketQueueEntry) {
            this.logger.warn('skipping bucket queue entry', {
                method: 'MongoQueueProcessor.processKafkaEntry',
                entry: sourceEntry.getLogInfo(),
            });
            return process.nextTick(done);
        }

        const bucketName = sourceEntry.getBucket();
        return this._mongoClient.getBucketAttributes(bucketName, this.logger,
        (err, data) => {
            if (err) {
                this.logger.error('error getting bucket owner details', {
                    method: 'MongoQueueProcessor.processKafkaEntry',
                    entry: sourceEntry.getLogInfo(),
                });
                return done(err);
            }
            const ownerInfo = {
                ownerId: data._owner,
                ownerDisplayName: data._ownerDisplayName,
            };
            if (sourceEntry instanceof DeleteOpQueueEntry) {
                return this._processDeleteOpQueueEntry(sourceEntry, done);
            }
            if (sourceEntry instanceof ObjectQueueEntry) {
                return this._processObjectQueueEntry(sourceEntry, ownerInfo,
                    done);
            }
            this.logger.warn('skipping unknown source entry',
                            { entry: sourceEntry.getLogInfo() });
            return process.nextTick(done);
        });
    }

    isReady() {
        return this._consumer && this._consumer.isReady();
    }
}

module.exports = MongoQueueProcessor;
<commit_msg>feature: ZENKO-1343 add owner info to object attributes<commit_after>'use strict'; // eslint-disable-line

const Logger = require('werelogs').Logger;
const errors = require('arsenal').errors;
const MongoClient = require('arsenal').storage
    .metadata.mongoclient.MongoClientInterface;

const BackbeatConsumer = require('../../lib/BackbeatConsumer');
const QueueEntry = require('../../lib/models/QueueEntry');
const DeleteOpQueueEntry = require('../../lib/models/DeleteOpQueueEntry');
const BucketQueueEntry = require('../../lib/models/BucketQueueEntry');
const BucketMdQueueEntry = require('../../lib/models/BucketMdQueueEntry');
const ObjectQueueEntry = require('../../lib/models/ObjectQueueEntry');

// TODO - ADD PREFIX BASED ON SOURCE
// april 6, 2018

/**
 * @class MongoQueueProcessor
 *
 * @classdesc Background task that processes entries from the
 * ingestion for kafka queue and pushes entries to mongo
 */
class MongoQueueProcessor {

    /**
     * @constructor
     * @param {Object} kafkaConfig - kafka configuration object
     * @param {String} kafkaConfig.hosts - list of kafka brokers
     *   as "host:port[,host:port...]"
     * @param {Object} mongoProcessorConfig - mongo processor configuration
     *   object
     * @param {String} mongoProcessorConfig.topic - topic name
     * @param {String} mongoProcessorConfig.groupId - kafka
     *   consumer group ID
     * @param {number} [mongoProcessorConfig.retry.timeoutS] -
     *  retry timeout in secs.
     * @param {number} [mongoProcessorConfig.retry.maxRetries] -
     *  max retries before giving up
     * @param {Object} [mongoProcessorConfig.retry.backoff] -
     *  backoff params
     * @param {number} [mongoProcessorConfig.retry.backoff.min] -
     *  min. backoff in ms.
     * @param {number} [mongoProcessorConfig.retry.backoff.max] -
     *  max. backoff in ms.
     * @param {number} [mongoProcessorConfig.retry.backoff.jitter] -
     *  randomness
     * @param {number} [mongoProcessorConfig.retry.backoff.factor] -
     *  backoff factor
     * @param {Object} mongoClientConfig - config for connecting to mongo
     * @param {String} site - site name
     */
    constructor(kafkaConfig, mongoProcessorConfig, mongoClientConfig, site) {
        this.kafkaConfig = kafkaConfig;
        this.mongoProcessorConfig = mongoProcessorConfig;
        this.mongoClientConfig = mongoClientConfig;
        this.site = site;

        this._consumer = null;
        this.logger =
            new Logger(`Backbeat:Ingestion:MongoProcessor:${this.site}`);
        this.mongoClientConfig.logger = this.logger;
        this._mongoClient = new MongoClient(this.mongoClientConfig);
    }

    /**
     * Start kafka consumer
     *
     * @return {undefined}
     */
    start() {
        this.logger.info('starting mongo queue processor');
        this._mongoClient.setup(err => {
            if (err) {
                this.logger.error('could not connect to MongoDB', { err });
                process.exit(1);
            }
            let consumerReady = false;
            this._consumer = new BackbeatConsumer({
                topic: this.mongoProcessorConfig.topic,
                groupId: `${this.mongoProcessorConfig.groupId}-${this.site}`,
                kafka: { hosts: this.kafkaConfig.hosts },
                queueProcessor: this.processKafkaEntry.bind(this),
            });
            this._consumer.on('error', () => {
                if (!consumerReady) {
                    this.logger.fatal('error starting mongo queue processor');
                    process.exit(1);
                }
            });
            this._consumer.on('ready', () => {
                consumerReady = true;
                this._consumer.subscribe();
                this.logger.info('mongo queue processor is ready');
            });
        });
    }

    /**
     * Stop kafka consumer and commit current offset
     *
     * @param {function} done - callback
     * @return {undefined}
     */
    stop(done) {
        if (!this._consumer) {
            return setImmediate(done);
        }
        return this._consumer.close(done);
    }

    /**
     * Update ingested entry metadata fields: owner-id, owner-display-name
     * @param {ObjectQueueEntry} entry - object queue entry object
     * @param {object} ownerInfo - owner details
     * @return {undefined}
     */
    _updateOwnerMD(entry, ownerInfo) {
        // zenko bucket owner information is being set on ingested md
        entry.setOwnerDisplayName(ownerInfo.ownerDisplayName);
        entry.setOwnerId(ownerInfo.ownerId);
    }

    /**
     * Process a delete object entry
     * @param {DeleteOpQueueEntry} sourceEntry - delete object entry
     * @param {function} done - callback(error)
     * @return {undefined}
     */
    _processDeleteOpQueueEntry(sourceEntry, done) {
        const bucket = sourceEntry.getBucket();
        const key = sourceEntry.getObjectVersionedKey();

        // Always call deleteObject with version params undefined so
        // that mongoClient will use deleteObjectNoVer which just deletes
        // the object without further manipulation/actions.
        // S3 takes care of the versioning logic so consuming the queue
        // is sufficient to replay the version logic in the consumer.
        return this._mongoClient.deleteObject(bucket, key, undefined,
            this.logger, err => {
                if (err) {
                    this.logger.error('error deleting object metadata ' +
                    'from mongo', { bucket, key, error: err.message });
                    return done(err);
                }
                this.logger.info('object metadata deleted from mongo',
                { bucket, key });
                return done();
            });
    }

    /**
     * Process an object entry
     * @param {ObjectQueueEntry} sourceEntry - object metadata entry
     * @param {object} ownerInfo - owner details
     * @param {function} done - callback(error)
     * @return {undefined}
     */
    _processObjectQueueEntry(sourceEntry, ownerInfo, done) {
        const bucket = sourceEntry.getBucket();
        // always use versioned key so putting full version state to mongo
        const key = sourceEntry.getObjectVersionedKey();

        // update necessary metadata fields before saving to Zenko MongoDB
        this._updateOwnerMD(sourceEntry, ownerInfo);

        const objVal = sourceEntry.getValue();
        // Always call putObject with version params undefined so
        // that mongoClient will use putObjectNoVer which just puts
        // the object without further manipulation/actions.
        // S3 takes care of the versioning logic so consuming the queue
        // is sufficient to replay the version logic in the consumer.
        return this._mongoClient.putObject(bucket, key, objVal, undefined,
            this.logger, err => {
                if (err) {
                    this.logger.error('error putting object metadata ' +
                    'to mongo', { error: err });
                    return done(err);
                }
                this.logger.info('object metadata put to mongo',
                { key });
                return done();
            });
    }

    /**
     * Put kafka queue entry into mongo
     *
     * @param {object} kafkaEntry - entry generated by ingestion populator
     * @param {string} kafkaEntry.key - kafka entry key
     * @param {string} kafkaEntry.value - kafka entry value
     * @param {function} done - callback function
     * @return {undefined}
     */
    processKafkaEntry(kafkaEntry, done) {
        const sourceEntry = QueueEntry.createFromKafkaEntry(kafkaEntry);
        if (sourceEntry.error) {
            this.logger.error('error processing source entry',
                              { error: sourceEntry.error });
            return process.nextTick(() => done(errors.InternalError));
        }
        // TODO-FIX:
        // Depends on the filter data. Need a way of determining the
        // zenko bucket.
        // if entry is for another site, simply skip/ignore
        if (this.site !== kafkaEntry.bucket) {
            return process.nextTick(done);
        }
        if (sourceEntry instanceof BucketMdQueueEntry) {
            this.logger.warn('skipping bucket md queue entry', {
                method: 'MongoQueueProcessor.processKafkaEntry',
                entry: sourceEntry.getLogInfo(),
            });
            return process.nextTick(done);
        } else if (sourceEntry instanceof BucketQueueEntry) {
            this.logger.warn('skipping bucket queue entry', {
                method: 'MongoQueueProcessor.processKafkaEntry',
                entry: sourceEntry.getLogInfo(),
            });
            return process.nextTick(done);
        }

        const bucketName = sourceEntry.getBucket();
        return this._mongoClient.getBucketAttributes(bucketName, this.logger,
        (err, data) => {
            if (err) {
                this.logger.error('error getting bucket owner details', {
                    method: 'MongoQueueProcessor.processKafkaEntry',
                    entry: sourceEntry.getLogInfo(),
                });
                return done(err);
            }
            const ownerInfo = {
                ownerId: data._owner,
                ownerDisplayName: data._ownerDisplayName,
            };
            if (sourceEntry instanceof DeleteOpQueueEntry) {
                return this._processDeleteOpQueueEntry(sourceEntry, done);
            }
            if (sourceEntry instanceof ObjectQueueEntry) {
                return this._processObjectQueueEntry(sourceEntry, ownerInfo,
                    done);
            }
            this.logger.warn('skipping unknown source entry',
                            { entry: sourceEntry.getLogInfo() });
            return process.nextTick(done);
        });
    }

    isReady() {
        return this._consumer && this._consumer.isReady();
    }
}

module.exports = MongoQueueProcessor;
<|endoftext|>


<commit_before>module.exports = {
  'rules': {
    // enforce spacing inside array brackets
    'array-bracket-spacing': [2, 'never'],
    // enforce one true brace style
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    // require camel case names
    'camelcase': [2, { 'properties': 'never' }],
    // enforce spacing before and after comma
    'comma-spacing': [2, { 'before': false, 'after': true }],
    // enforce one true comma style
    'comma-style': [2, 'last'],
    // disallow padding inside computed properties
    'computed-property-spacing': [2, 'never'],
    // enforces consistent naming when capturing the current execution context
    'consistent-this': 0,
    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 2,
    // require function expressions to have a name
    'func-names': 1,
    // enforces use of function declarations or expressions
    'func-style': 0,
    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 0,
    // this option sets a specific tab width for your code
    // http://eslint.org/docs/rules/indent
    'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [2, 'prefer-double'],
    // enforces spacing between keys and values in object literal properties
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    // require a space before & after certain keywords
    'keyword-spacing': [2, {
      'before': true,
      'after': true,
      'overrides': {
        'return': { 'after': true },
        'throw': { 'after': true },
        'case': { 'after': true }
      }
    }],
    // enforces empty lines around comments
    'lines-around-comment': 0,
    // disallow mixed 'LF' and 'CRLF' as linebreaks
    'linebreak-style': 0,
    // specify the maximum length of a line in your program
    // http://eslint.org/docs/rules/max-len
    'max-len': [2, 100, 2, {
      'ignoreUrls': true,
      'ignoreComments': false
    }],
    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 0,
    // require a capital letter for constructors
    'new-cap': [2, { 'newIsCap': true }],
    // disallow the omission of parentheses when invoking a constructor with no arguments
    'new-parens': 0,
    // allow/disallow an empty newline after var statement
    'newline-after-var': 0,
    // http://eslint.org/docs/rules/newline-before-return
    'newline-before-return': 0,
    // enforces new line after each method call in the chain to make it
    // more readable and easy to maintain
    // http://eslint.org/docs/rules/newline-per-chained-call
    'newline-per-chained-call': [0, { 'ignoreChainWithDepth': 3 }],
    // disallow use of the Array constructor
    'no-array-constructor': 2,
    // disallow use of the continue statement
    'no-continue': 0,
    // disallow comments inline after code
    'no-inline-comments': 0,
    // disallow if as the only statement in an else block
    'no-lonely-if': 0,
    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 2,
    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
    // disallow nested ternary expressions
    'no-nested-ternary': 2,
    // disallow use of the Object constructor
    'no-new-object': 2,
    // disallow space between function identifier and application
    'no-spaced-func': 2,
    // disallow the use of ternary operators
    'no-ternary': 0,
    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 2,
    // disallow dangling underscores in identifiers
    'no-underscore-dangle': [2, { 'allowAfterThis': false }],
    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
    // disallow whitespace before properties
    // http://eslint.org/docs/rules/no-whitespace-before-property
    'no-whitespace-before-property': 2,
    // require padding inside curly braces
    'object-curly-spacing': [2, 'always'],
    // allow just one var statement per function
    'one-var': [2, 'never'],
    // require a newline around variable declaration
    // http://eslint.org/docs/rules/one-var-declaration-per-line
    'one-var-declaration-per-line': [2, 'always'],
    // require assignment operator shorthand where possible or prohibit it entirely
    'operator-assignment': 0,
    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 0,
    // enforce padding within blocks
    'padded-blocks': [2, 'never'],
    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
    // specify whether double or single quotes should be used
    'quotes': [2, 'single', 'avoid-escape'],
    // require identifiers to match the provided regular expression
    'id-match': 0,
    // enforce spacing before and after semicolons
    'semi-spacing': [2, { 'before': false, 'after': true }],
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'always'],
    // sort variables within the same declaration block
    'sort-vars': 0,
    // require or disallow space before blocks
    'space-before-blocks': 2,
    // require or disallow space before function opening parenthesis
    // http://eslint.org/docs/rules/space-before-function-paren
    'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
    // require or disallow spaces inside parentheses
    'space-in-parens': [2, 'never'],
    // require spaces around operators
    'space-infix-ops': 2,
    // Require or disallow spaces before/after unary operators
    'space-unary-ops': 0,
    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': [2, 'always', {
      'exceptions': ['-', '+'],
      'markers': ['=', '!']           // space here to support sprockets directives
    }],
    // require regex literals to be wrapped in parentheses
    'wrap-regex': 0
  }
};
<commit_msg>[eslint config] [semver-minor] enforce spacing inside single-line blocks.<commit_after>module.exports = {
  'rules': {
    // enforce spacing inside array brackets
    'array-bracket-spacing': [2, 'never'],
    // enforce spacing inside single-line blocks
    // http://eslint.org/docs/rules/block-spacing
    'block-spacing': [2, 'always'],
    // enforce one true brace style
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    // require camel case names
    'camelcase': [2, { 'properties': 'never' }],
    // enforce spacing before and after comma
    'comma-spacing': [2, { 'before': false, 'after': true }],
    // enforce one true comma style
    'comma-style': [2, 'last'],
    // disallow padding inside computed properties
    'computed-property-spacing': [2, 'never'],
    // enforces consistent naming when capturing the current execution context
    'consistent-this': 0,
    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 2,
    // require function expressions to have a name
    'func-names': 1,
    // enforces use of function declarations or expressions
    'func-style': 0,
    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 0,
    // this option sets a specific tab width for your code
    // http://eslint.org/docs/rules/indent
    'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [2, 'prefer-double'],
    // enforces spacing between keys and values in object literal properties
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    // require a space before & after certain keywords
    'keyword-spacing': [2, {
      'before': true,
      'after': true,
      'overrides': {
        'return': { 'after': true },
        'throw': { 'after': true },
        'case': { 'after': true }
      }
    }],
    // enforces empty lines around comments
    'lines-around-comment': 0,
    // disallow mixed 'LF' and 'CRLF' as linebreaks
    'linebreak-style': 0,
    // specify the maximum length of a line in your program
    // http://eslint.org/docs/rules/max-len
    'max-len': [2, 100, 2, {
      'ignoreUrls': true,
      'ignoreComments': false
    }],
    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 0,
    // require a capital letter for constructors
    'new-cap': [2, { 'newIsCap': true }],
    // disallow the omission of parentheses when invoking a constructor with no arguments
    'new-parens': 0,
    // allow/disallow an empty newline after var statement
    'newline-after-var': 0,
    // http://eslint.org/docs/rules/newline-before-return
    'newline-before-return': 0,
    // enforces new line after each method call in the chain to make it
    // more readable and easy to maintain
    // http://eslint.org/docs/rules/newline-per-chained-call
    'newline-per-chained-call': [0, { 'ignoreChainWithDepth': 3 }],
    // disallow use of the Array constructor
    'no-array-constructor': 2,
    // disallow use of the continue statement
    'no-continue': 0,
    // disallow comments inline after code
    'no-inline-comments': 0,
    // disallow if as the only statement in an else block
    'no-lonely-if': 0,
    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 2,
    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
    // disallow nested ternary expressions
    'no-nested-ternary': 2,
    // disallow use of the Object constructor
    'no-new-object': 2,
    // disallow space between function identifier and application
    'no-spaced-func': 2,
    // disallow the use of ternary operators
    'no-ternary': 0,
    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 2,
    // disallow dangling underscores in identifiers
    'no-underscore-dangle': [2, { 'allowAfterThis': false }],
    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
    // disallow whitespace before properties
    // http://eslint.org/docs/rules/no-whitespace-before-property
    'no-whitespace-before-property': 2,
    // require padding inside curly braces
    'object-curly-spacing': [2, 'always'],
    // allow just one var statement per function
    'one-var': [2, 'never'],
    // require a newline around variable declaration
    // http://eslint.org/docs/rules/one-var-declaration-per-line
    'one-var-declaration-per-line': [2, 'always'],
    // require assignment operator shorthand where possible or prohibit it entirely
    'operator-assignment': 0,
    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 0,
    // enforce padding within blocks
    'padded-blocks': [2, 'never'],
    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
    // specify whether double or single quotes should be used
    'quotes': [2, 'single', 'avoid-escape'],
    // require identifiers to match the provided regular expression
    'id-match': 0,
    // enforce spacing before and after semicolons
    'semi-spacing': [2, { 'before': false, 'after': true }],
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'always'],
    // sort variables within the same declaration block
    'sort-vars': 0,
    // require or disallow space before blocks
    'space-before-blocks': 2,
    // require or disallow space before function opening parenthesis
    // http://eslint.org/docs/rules/space-before-function-paren
    'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
    // require or disallow spaces inside parentheses
    'space-in-parens': [2, 'never'],
    // require spaces around operators
    'space-infix-ops': 2,
    // Require or disallow spaces before/after unary operators
    'space-unary-ops': 0,
    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': [2, 'always', {
      'exceptions': ['-', '+'],
      'markers': ['=', '!']           // space here to support sprockets directives
    }],
    // require regex literals to be wrapped in parentheses
    'wrap-regex': 0
  }
};
<|endoftext|>


<commit_before>function CustomFileBrowser(field_name, url, type, win) {

    var cmsURL = "/admin/filebrowser/browse/?pop=2";
    cmsURL = cmsURL + "&type=" + type;

    tinyMCE.activeEditor.windowManager.open({
        file: cmsURL,
        width: 820,  // Your dimensions may differ - toy around with them!
        height: 500,
        resizable: "yes",
        scrollbars: "yes",
        inline: "no",  // This parameter only has an effect if you use the inlinepopups plugin!
        close_previous: "no"
    }, {
        window: win,
        input: field_name,
        editor_id: tinyMCE.selectedInstance.editorId
    });
    return false;
}

tinyMCE.init({
    mode: "textareas",
    theme: "advanced",
    language: "en",
    skin: "o2k7",
    browsers: "gecko",
    dialog_type: "modal",
    object_resizing: true,
    cleanup_on_startup: true,
    forced_root_block: "p",
    remove_trailing_nbsp: true,
    theme_advanced_toolbar_location: "top",
    theme_advanced_toolbar_align: "left",
    theme_advanced_statusbar_location: "none",
    theme_advanced_buttons1: "formatselect,bold,italic,underline,bullist,numlist,undo,redo,link,unlink,image,code,fullscreen,pasteword,media,charmap",
    theme_advanced_buttons2: "",
    theme_advanced_buttons3: "",
    theme_advanced_path: false,
    theme_advanced_blockformats: "p,h2,h3,h4,h5,h6",
    width: '700',
    height: '200',
    plugins: "advimage,advlink,fullscreen,visualchars,paste,media,template,searchreplace",
    advimage_styles: "Linksbündig neben Text=img_left;Rechtsbündig neben Text=img_right;Eigener Block=img_block",
    advlink_styles: "internal (sehmaschine.net)=internal;external (link to an external site)=external",
    advimage_update_dimensions_onchange: true,
    file_browser_callback: "CustomFileBrowser",
    relative_urls: false,
    valid_elements : "" +
    "-p," +
    "a[href|target=_blank|class]," +
    "-strong/-b," +
    "-em/-i," +
    "-u," +
    "-ol," +
    "-ul," +
    "-li," +
    "br," +
    "img[class|src|alt=|width|height]," +
    "-h2,-h3,-h4," +
    "-pre," +
    "-code," +
    "-div",
    extended_valid_elements: "" +
    "a[name|class|href|target|title|onclick]," +
    "img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name]," +
    "br[clearfix]," +
    "-p[class<clearfix?summary?code]," +
    "h2[class<clearfix],h3[class<clearfix],h4[class<clearfix]," +
    "ul[class<clearfix],ol[class<clearfix]," +
    "div[class],"
});

<commit_msg>Hard code image browser popup dialog to utilize business photo URL<commit_after>function CustomFileBrowser(field_name, url, type, win) {

    var cmsURL = "/admin/business/photo/add/?_popup=1";
    cmsURL = cmsURL + "&type=" + type;

    tinyMCE.activeEditor.windowManager.open({
        file: cmsURL,
        width: 820,  // Your dimensions may differ - toy around with them!
        height: 500,
        resizable: "yes",
        scrollbars: "yes",
        inline: "no",  // This parameter only has an effect if you use the inlinepopups plugin!
        close_previous: "no"
    }, {
        window: win,
        input: field_name,
        editor_id: tinyMCE.selectedInstance.editorId
    });
    return false;
}

tinyMCE.init({
    mode: "textareas",
    theme: "advanced",
    language: "en",
    skin: "o2k7",
    browsers: "gecko",
    dialog_type: "modal",
    object_resizing: true,
    cleanup_on_startup: true,
    forced_root_block: "p",
    remove_trailing_nbsp: true,
    theme_advanced_toolbar_location: "top",
    theme_advanced_toolbar_align: "left",
    theme_advanced_statusbar_location: "none",
    theme_advanced_buttons1: "formatselect,bold,italic,underline,bullist,numlist,undo,redo,link,unlink,image,code,fullscreen,pasteword,media,charmap",
    theme_advanced_buttons2: "",
    theme_advanced_buttons3: "",
    theme_advanced_path: false,
    theme_advanced_blockformats: "p,h2,h3,h4,h5,h6",
    width: '700',
    height: '200',
    plugins: "advimage,advlink,fullscreen,visualchars,paste,media,template,searchreplace",
    advimage_styles: "Linksbündig neben Text=img_left;Rechtsbündig neben Text=img_right;Eigener Block=img_block",
    advlink_styles: "internal (sehmaschine.net)=internal;external (link to an external site)=external",
    advimage_update_dimensions_onchange: true,
    file_browser_callback: "CustomFileBrowser",
    relative_urls: false,
    valid_elements : "" +
    "-p," +
    "a[href|target=_blank|class]," +
    "-strong/-b," +
    "-em/-i," +
    "-u," +
    "-ol," +
    "-ul," +
    "-li," +
    "br," +
    "img[class|src|alt=|width|height]," +
    "-h2,-h3,-h4," +
    "-pre," +
    "-code," +
    "-div",
    extended_valid_elements: "" +
    "a[name|class|href|target|title|onclick]," +
    "img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name]," +
    "br[clearfix]," +
    "-p[class<clearfix?summary?code]," +
    "h2[class<clearfix],h3[class<clearfix],h4[class<clearfix]," +
    "ul[class<clearfix],ol[class<clearfix]," +
    "div[class],"
});

<|endoftext|>


<commit_before>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');

var db = module.parent.require('./database'); // NodeBB / src / database 참고.
var au = module.parent.require('./controllers/authentication'); // NodeBB / src / controllers 참고.

var winston = require('winston');
var jwt = require('jsonwebtoken');
// 모듈 객체.
var plugin = {};

plugin.init = function (params, callback)	{
	winston.info('Start community..');

	callback();
}

plugin.verifyUser = function (token, callback)	{
	jwt.verify(token, 'secret', function (err, user_info)	{
		if (err)	{
			winston.error(err);

			return false;
		}

		winston.info('User information is ' + user_info);
		// MongoDB (nodebb) 에서 유저가 존재하는 지 확인한다. 기존에 test 란 유저는 존재했기에 test102로 잠깐 바꿔서 테스트한다.
		var user_exist = db.getObjectField(user_info.institute_short + ':uid', user_info.id.replace('test', 'test102'), function (err, isExist)	{
			if (err)	{
				winston.error('During checking user error : ', err);

				return false;
			}

			if (isExist)	{
				winston.info('Exist user ' + isExist);
				// 존재할 경우 로그인을 실행한다.
				callback(isExist);
			} else {
				// 존재 하지 않을 경우 NodeBB 플러그인의 유저 생성을 사용하여, uid 를 만들고 이를 MongoDB 에 넣는다.
				// 그리고 로그인을 실행한다.
				var test = user_info.id.replace('test', 'test102');

				user.create({
				username: user_info.name,
				id: test,
				email: test,
				institute_short: user_info.institute_short
				}, function (err, uid)	{
					if (err)	{
						winston.error('Creating user ' + test + ' error : ' + err);

						return false;
					}
					// TODO.
					// 이메일이 중복되었을 경우, 에러가 발생하는데 이를 방지할 대책을 세워야 한다.
					winston.info('Success create uid : ', uid);

					db.setObjectField(user_info.institute_short + ':uid', test, uid);

					callback(uid);
				});
			}
		});
	});
}

// 이 함수는 NodeBB 에서 어떤 동작 또는 페이지이동 때마다 호출되므로 계속해서 토큰을 받아오고 유저 유효성 검사를 할 것이다.
// 그러므로 함수 첫줄에 세션 확인을 하는 구문을 만들어 같은 세션일 경우 유저 유효성 검사를 넘어가도록 한다.
plugin.addMiddleware = function (req, res, next)	{
	// 이미 있는 세션일 경우 요청 프로퍼티에 user 와 user 안에 uid 가 존재 한다.
	// TODO.
	// 로그아웃 후 재로그인한뒤에, 창을 닫고 바이오클라우드에서 다시 커뮤니티로 접속하면 세션관련 리프레쉬가 발생한다.
	// 이 경우 세션이 변경됨을 확인하고 리프레쉬를 해줘야한다.
	var hasSession = req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && parseInt(req.user.uid, 10) > 10;

	if (hasSession)	{
		winston.info('Has Already session');
		// 기존 유저가 접속되어있는 경우 세션확인 후 유저 유효성 검사 없이 진행한다.
		return next();
	} else {
		// 별도의 쿠키를 관리하지 않으므로, 로그아웃 처리를 위하여 세션이 해제 됨과 동시에 쿼리받는 데이터도 없어진다.
		// 그래서 로그아웃 후에 현재 함수 호출시 조건으로 쿼리데이터를 선정하였다.
		if (!req.query.t) {
			return next();
		} else {
			// TODO.
			// 현재는 정해진 포맷에 맞춰 유저정보를 받고 입력하지만,
			// 추후에는 사용자가 포맷을 정의하고 해당 포맷에 맞게 입력받게 만들어야 한다.
			plugin.verifyUser(req.query.t, function (uid) {
				winston.info('User ' + uid + ' is verified');

				au.doLogin(req, uid, next);
			});
		}
	}
};
// 로그아웃 함수.
plugin.doLogout = function (data, callback)	{
	winston.info('Do logout..');

	if (typeof callback === 'function')	{
		callback();
	} else {
		return true;
	}
}

module.exports = plugin;<commit_msg>20170124085115<commit_after>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');

var db = module.parent.require('./database'); // NodeBB / src / database 참고.
var au = module.parent.require('./controllers/authentication'); // NodeBB / src / controllers 참고.

var winston = require('winston');
var jwt = require('jsonwebtoken');
// 모듈 객체.
var plugin = {};

plugin.init = function (params, callback)	{
	winston.info('Start community..');

	callback();
}

plugin.verifyUser = function (token, callback)	{
	jwt.verify(token, 'secret', function (err, user_info)	{
		if (err)	{
			winston.error(err);

			return false;
		}

		winston.info('User information is ' + user_info);
		// MongoDB (nodebb) 에서 유저가 존재하는 지 확인한다. 기존에 test 란 유저는 존재했기에 test102로 잠깐 바꿔서 테스트한다.
		var user_exist = db.getObjectField(user_info.institute_short + ':uid', user_info.id.replace('test', 'test102'), function (err, isExist)	{
			if (err)	{
				winston.error('During checking user error : ', err);

				return false;
			}

			if (isExist)	{
				winston.info('Exist user ' + isExist);
				// 존재할 경우 로그인을 실행한다.
				callback(isExist);
			} else {
				// 존재 하지 않을 경우 NodeBB 플러그인의 유저 생성을 사용하여, uid 를 만들고 이를 MongoDB 에 넣는다.
				// 그리고 로그인을 실행한다.
				var test = user_info.id.replace('test', 'test102');

				user.create({
				username: user_info.name,
				id: test,
				email: test,
				institute_short: user_info.institute_short
				}, function (err, uid)	{
					if (err)	{
						winston.error('Creating user ' + test + ' error : ' + err);

						return false;
					}
					// TODO.
					// 이메일이 중복되었을 경우, 에러가 발생하는데 이를 방지할 대책을 세워야 한다.
					winston.info('Success create uid : ', uid);

					db.setObjectField(user_info.institute_short + ':uid', test, uid);

					callback(uid);
				});
			}
		});
	});
}

// 이 함수는 NodeBB 에서 어떤 동작 또는 페이지이동 때마다 호출되므로 계속해서 토큰을 받아오고 유저 유효성 검사를 할 것이다.
// 그러므로 함수 첫줄에 세션 확인을 하는 구문을 만들어 같은 세션일 경우 유저 유효성 검사를 넘어가도록 한다.
plugin.addMiddleware = function (req, res, next)	{
	// 이미 있는 세션일 경우 요청 프로퍼티에 user 와 user 안에 uid 가 존재 한다.
	// TODO.
	// 로그아웃 후 재로그인한뒤에, 창을 닫고 바이오클라우드에서 다시 커뮤니티로 접속하면 세션관련 리프레쉬가 발생한다.
	// 이 경우 세션이 변경됨을 확인하고 리프레쉬를 해줘야한다.
	var hasSession = req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && parseInt(req.user.uid, 10) > 10;

	console.log(req.url);

	if (hasSession)	{
		winston.info('Has Already session');
		// 기존 유저가 접속되어있는 경우 세션확인 후 유저 유효성 검사 없이 진행한다.
		return next();
	} else {
		// 별도의 쿠키를 관리하지 않으므로, 로그아웃 처리를 위하여 세션이 해제 됨과 동시에 쿼리받는 데이터도 없어진다.
		// 그래서 로그아웃 후에 현재 함수 호출시 조건으로 쿼리데이터를 선정하였다.
		if (!req.query.t) {
			return next();
		} else {
			// TODO.
			// 현재는 정해진 포맷에 맞춰 유저정보를 받고 입력하지만,
			// 추후에는 사용자가 포맷을 정의하고 해당 포맷에 맞게 입력받게 만들어야 한다.
			plugin.verifyUser(req.query.t, function (uid) {
				winston.info('User ' + uid + ' is verified');

				au.doLogin(req, uid, next);
			});
		}
	}
};
// 로그아웃 함수.
plugin.doLogout = function (data, callback)	{
	winston.info('Do logout..');

	if (typeof callback === 'function')	{
		callback();
	} else {
		return true;
	}
}

module.exports = plugin;<|endoftext|>


<commit_before>"use strict";

var passport = module.parent.require('passport'),
	passportTotp = require('passport-totp').Strategy,
	loggedIn = module.parent.require('connect-ensure-login'),

	db = module.parent.require('./database'),
	nconf = module.parent.require('nconf'),
	async = module.parent.require('async'),
	user = module.parent.require('./user'),
	notifications = module.parent.require('./notifications'),
	meta = module.parent.require('./meta'),
	utils = module.parent.require('../public/src/utils'),
	translator = module.parent.require('../public/src/modules/translator'),
	routeHelpers = module.parent.require('./controllers/helpers'),

	SocketPlugins = require.main.require('./src/socket.io/plugins'),
	plugin = {};

plugin.init = function(params, callback) {
	var router = params.router,
		hostMiddleware = params.middleware,
		hostControllers = params.controllers,
		hostHelpers = require.main.require('./src/routes/helpers'),
		controllers = require('./lib/controllers'),
		middlewares = require('./lib/middlewares');

	// ACP
	router.get('/admin/plugins/2factor', hostMiddleware.admin.buildHeader, controllers.renderAdminPage);
	router.get('/api/admin/plugins/2factor', controllers.renderAdminPage);

	// UCP
	hostHelpers.setupPageRoute(router, '/user/:userslug/2factor', hostMiddleware, [hostMiddleware.requireUser, hostMiddleware.exposeUid], controllers.renderSettings);

	// 2fa Login
	router.get('/login/2fa', hostMiddleware.buildHeader, loggedIn.ensureLoggedIn(), controllers.renderLogin);
	router.get('/api/login/2fa', loggedIn.ensureLoggedIn(), controllers.renderLogin);
	router.post('/login/2fa', loggedIn.ensureLoggedIn(), controllers.processLogin, function(req, res) {
		req.session.tfa = true;
		res.redirect(nconf.get('relative_path') + (req.query.next || '/'));
	});

	// 2fa backups codes
	router.get('/login/2fa/backup', hostMiddleware.buildHeader, loggedIn.ensureLoggedIn(), controllers.renderBackup);
	router.get('/api/login/2fa/backup', loggedIn.ensureLoggedIn(), controllers.renderBackup);
	router.post('/login/2fa/backup', loggedIn.ensureLoggedIn(), controllers.processBackup, function(req, res) {
		req.session.tfa = true;
		res.redirect(nconf.get('relative_path') + (req.query.next || '/'));
	});
	router.put('/login/2fa/backup', hostMiddleware.requireUser, middlewares.requireSecondFactor, hostMiddleware.applyCSRF, controllers.generateBackupCodes);

	// Websockets
	SocketPlugins['2factor'] = require('./websockets');

	// Login Strategy
	passport.use(new passportTotp(
		function(user, done) {
			plugin.get(user.uid, function(err, key) {
				if (err) { return done(err); }
				return done(null, key, 30);
			});
		}
	));

	callback();
};

plugin.addAdminNavigation = function(header, callback) {
	translator.translate('[[2factor:title]]', function(title) {
		header.plugins.push({
			route: '/plugins/2factor',
			icon: 'fa-lock',
			name: title
		});

		callback(null, header);
	});
};

plugin.addProfileItem = function(data, callback) {
	translator.translate('[[2factor:title]]', function(title) {
		data.links.push({
			id: '2factor',
			route: '2factor',
			icon: 'fa-lock',
			name: title,
			visibility: {
				self: true,
				other: false,
				moderator: false,
				globalMod: false,
				admin: false,
			}
		});

		callback(null, data);
	});
};

plugin.get = function(uid, callback) {
	db.getObjectField('2factor:uid:key', uid, callback);
};

plugin.save = function(uid, key, callback) {
	db.setObjectField('2factor:uid:key', uid, key, callback);
};

plugin.hasKey = function(uid, callback) {
	db.isObjectField('2factor:uid:key', uid, callback);
};

plugin.generateBackupCodes = function(uid, callback) {
	var set = '2factor:uid:' + uid + ':backupCodes',
		codes = [],
		code;

	for(var x=0;x<5;x++) {
		code = utils.generateUUID().replace('-', '').slice(0, 10);
		codes.push(code);
	}

	async.series([
		async.apply(db.delete, set),		// Invalidate all old codes
		async.apply(db.setAdd, set, codes),	// Save new codes
		function(next) {
			notifications.create({
				bodyShort: '[[2factor:notification.backupCode.generated]]',
				bodyLong: '',
				nid: '2factor.backupCode.generated-' + uid + '-' + Date.now(),
				from: uid,
				path: '/'
			}, function(err, notification) {
				if (!err && notification) {
					notifications.push(notification, [uid], next);
				}
			});
		}
	], function(err) {
		callback(err, codes);
	});
};

plugin.useBackupCode = function(code, uid, callback) {
	var set = '2factor:uid:' + uid + ':backupCodes';

	async.waterfall([
		async.apply(db.isSetMember, set, code),
		function(valid, next) {
			if (valid) {
				// Invalidate this backup code
				db.setRemove(set, code, function(err) {
					next(err, valid);
				});

				notifications.create({
					bodyShort: '[[2factor:notification.backupCode.used]]',
					bodyLong: '',
					nid: '2factor.backupCode.used-' + uid + '-' + Date.now(),
					from: uid,
					path: '/'
				}, function(err, notification) {
					if (!err && notification) {
						notifications.push(notification, [uid]);
					}
				});
			} else {
				next(null, valid);
			}
		}
	], callback);
};

plugin.disassociate = function(uid, callback) {
	async.parallel([
		async.apply(db.deleteObjectField, '2factor:uid:key', uid),
		async.apply(db.delete, '2factor:uid:' + uid + ':backupCodes')
	], callback);
};

plugin.check = function(req, res, next) {
	if (!req.user || req.session.tfa === true) {
		return next();
	}

	plugin.hasKey(req.user.uid, function(err, hasKey) {
		if (hasKey) {
			// Account has TFA, redirect to login
			routeHelpers.redirect(res, '/login/2fa' + (res.locals.isAPI ? '?next=' + req.url.replace('/api', '') : ''));
		} else {
			// No TFA setup
			return next();
		}
	})
};

plugin.clearSession = function(data, callback) {
	if (data.req.session) {
		delete data.req.session.tfa;
	}

	setImmediate(callback);
};

plugin.getUsers = function(callback) {
	async.waterfall([
		async.apply(db.getObjectKeys, '2factor:uid:key'),
		function(uids, next) {
			user.getUsersFields(uids, ['username', 'userslug', 'picture'], next);
		}
	], callback);
};

plugin.updateTitle = function(data, callback) {
	translator.translate('[[2factor:title]]', function(title) {
		if (data.fragment.match(/^user\/.+\/2factor/)) {
			data.parsed = title;
		}

		callback(null, data);
	});
};

module.exports = plugin;
<commit_msg>Fix Title Parse.<commit_after>"use strict";

var passport = module.parent.require('passport'),
	passportTotp = require('passport-totp').Strategy,
	loggedIn = module.parent.require('connect-ensure-login'),

	db = module.parent.require('./database'),
	nconf = module.parent.require('nconf'),
	async = module.parent.require('async'),
	user = module.parent.require('./user'),
	notifications = module.parent.require('./notifications'),
	meta = module.parent.require('./meta'),
	utils = module.parent.require('../public/src/utils'),
	translator = module.parent.require('../public/src/modules/translator'),
	routeHelpers = module.parent.require('./controllers/helpers'),

	SocketPlugins = require.main.require('./src/socket.io/plugins'),
	plugin = {};

plugin.init = function(params, callback) {
	var router = params.router,
		hostMiddleware = params.middleware,
		hostControllers = params.controllers,
		hostHelpers = require.main.require('./src/routes/helpers'),
		controllers = require('./lib/controllers'),
		middlewares = require('./lib/middlewares');

	// ACP
	router.get('/admin/plugins/2factor', hostMiddleware.admin.buildHeader, controllers.renderAdminPage);
	router.get('/api/admin/plugins/2factor', controllers.renderAdminPage);

	// UCP
	hostHelpers.setupPageRoute(router, '/user/:userslug/2factor', hostMiddleware, [hostMiddleware.requireUser, hostMiddleware.exposeUid], controllers.renderSettings);

	// 2fa Login
	router.get('/login/2fa', hostMiddleware.buildHeader, loggedIn.ensureLoggedIn(), controllers.renderLogin);
	router.get('/api/login/2fa', loggedIn.ensureLoggedIn(), controllers.renderLogin);
	router.post('/login/2fa', loggedIn.ensureLoggedIn(), controllers.processLogin, function(req, res) {
		req.session.tfa = true;
		res.redirect(nconf.get('relative_path') + (req.query.next || '/'));
	});

	// 2fa backups codes
	router.get('/login/2fa/backup', hostMiddleware.buildHeader, loggedIn.ensureLoggedIn(), controllers.renderBackup);
	router.get('/api/login/2fa/backup', loggedIn.ensureLoggedIn(), controllers.renderBackup);
	router.post('/login/2fa/backup', loggedIn.ensureLoggedIn(), controllers.processBackup, function(req, res) {
		req.session.tfa = true;
		res.redirect(nconf.get('relative_path') + (req.query.next || '/'));
	});
	router.put('/login/2fa/backup', hostMiddleware.requireUser, middlewares.requireSecondFactor, hostMiddleware.applyCSRF, controllers.generateBackupCodes);

	// Websockets
	SocketPlugins['2factor'] = require('./websockets');

	// Login Strategy
	passport.use(new passportTotp(
		function(user, done) {
			plugin.get(user.uid, function(err, key) {
				if (err) { return done(err); }
				return done(null, key, 30);
			});
		}
	));

	callback();
};

plugin.addAdminNavigation = function(header, callback) {
	translator.translate('[[2factor:title]]', function(title) {
		header.plugins.push({
			route: '/plugins/2factor',
			icon: 'fa-lock',
			name: title
		});

		callback(null, header);
	});
};

plugin.addProfileItem = function(data, callback) {
	translator.translate('[[2factor:title]]', function(title) {
		data.links.push({
			id: '2factor',
			route: '2factor',
			icon: 'fa-lock',
			name: title,
			visibility: {
				self: true,
				other: false,
				moderator: false,
				globalMod: false,
				admin: false,
			}
		});

		callback(null, data);
	});
};

plugin.get = function(uid, callback) {
	db.getObjectField('2factor:uid:key', uid, callback);
};

plugin.save = function(uid, key, callback) {
	db.setObjectField('2factor:uid:key', uid, key, callback);
};

plugin.hasKey = function(uid, callback) {
	db.isObjectField('2factor:uid:key', uid, callback);
};

plugin.generateBackupCodes = function(uid, callback) {
	var set = '2factor:uid:' + uid + ':backupCodes',
		codes = [],
		code;

	for(var x=0;x<5;x++) {
		code = utils.generateUUID().replace('-', '').slice(0, 10);
		codes.push(code);
	}

	async.series([
		async.apply(db.delete, set),		// Invalidate all old codes
		async.apply(db.setAdd, set, codes),	// Save new codes
		function(next) {
			notifications.create({
				bodyShort: '[[2factor:notification.backupCode.generated]]',
				bodyLong: '',
				nid: '2factor.backupCode.generated-' + uid + '-' + Date.now(),
				from: uid,
				path: '/'
			}, function(err, notification) {
				if (!err && notification) {
					notifications.push(notification, [uid], next);
				}
			});
		}
	], function(err) {
		callback(err, codes);
	});
};

plugin.useBackupCode = function(code, uid, callback) {
	var set = '2factor:uid:' + uid + ':backupCodes';

	async.waterfall([
		async.apply(db.isSetMember, set, code),
		function(valid, next) {
			if (valid) {
				// Invalidate this backup code
				db.setRemove(set, code, function(err) {
					next(err, valid);
				});

				notifications.create({
					bodyShort: '[[2factor:notification.backupCode.used]]',
					bodyLong: '',
					nid: '2factor.backupCode.used-' + uid + '-' + Date.now(),
					from: uid,
					path: '/'
				}, function(err, notification) {
					if (!err && notification) {
						notifications.push(notification, [uid]);
					}
				});
			} else {
				next(null, valid);
			}
		}
	], callback);
};

plugin.disassociate = function(uid, callback) {
	async.parallel([
		async.apply(db.deleteObjectField, '2factor:uid:key', uid),
		async.apply(db.delete, '2factor:uid:' + uid + ':backupCodes')
	], callback);
};

plugin.check = function(req, res, next) {
	if (!req.user || req.session.tfa === true) {
		return next();
	}

	plugin.hasKey(req.user.uid, function(err, hasKey) {
		if (hasKey) {
			// Account has TFA, redirect to login
			routeHelpers.redirect(res, '/login/2fa' + (res.locals.isAPI ? '?next=' + req.url.replace('/api', '') : ''));
		} else {
			// No TFA setup
			return next();
		}
	})
};

plugin.clearSession = function(data, callback) {
	if (data.req.session) {
		delete data.req.session.tfa;
	}

	setImmediate(callback);
};

plugin.getUsers = function(callback) {
	async.waterfall([
		async.apply(db.getObjectKeys, '2factor:uid:key'),
		function(uids, next) {
			user.getUsersFields(uids, ['username', 'userslug', 'picture'], next);
		}
	], callback);
};

plugin.updateTitle = function (data, callback) {
	translator.translate('[[2factor:title]]', function (title) {
		if (data.templateData.url.match(/user\/.+\/2factor/)) {
			data.templateData.title = title;
		}
		callback(null, data);
	});
};
module.exports = plugin;
<|endoftext|>


<commit_before>var	Akismet = require('akismet'),
    Honeypot = require('project-honeypot'),
	simpleRecaptcha = require('simple-recaptcha'),
    pluginData = require('./plugin.json'),
    winston = module.parent.require('winston'),
    nconf = module.parent.require('nconf'),
    async = module.parent.require('async'),
    Meta = module.parent.require('./meta'),
    akismet, honeypot, recaptchaArgs, pluginSettings,
	Plugin = {};

pluginData.nbbId = pluginData.id.replace(/nodebb-plugin-/, '');

var util = {
		keys: function(obj, props, value) {
			if(props == null || obj == null)
				return undefined;

			var i = props.indexOf(".");
			if( i == -1 ) {
				if(value !== undefined)
					obj[props] = value;
				return obj[props];
			}
			var prop = props.slice(0, i),
				newProps = props.slice(i + 1);

			if(props !== undefined && !(obj[prop] instanceof Object) )
				obj[prop] = {};

			return util.keys(obj[prop], newProps, value);
		}
};

Plugin.load = function(app, middleware, controllers) {

    var render = function(req, res, next) {
        res.render('admin/plugins/' + pluginData.nbbId, pluginData || {});
    };

    Meta.settings.get(pluginData.nbbId, function(err, settings) {
		if (!err && settings) {
            if (settings.akismetEnabled === 'on') {
                if (settings.akismetApiKey) {
                    akismet = require('akismet').client({blog: nconf.get('base_url'), apiKey: settings.akismetApiKey});
                    akismet.verifyKey(function(err, verified) {
                        if (!verified) {
                            winston.error('[plugins/' + pluginData.nbbId + '] Unable to verify Akismet API key.');
                            akismet = null;
                        }
                    });
                } else {
                    winston.error('[plugins/' + pluginData.nbbId + '] Akismet API Key not set!');
                }
            }

            if (settings.honeypotEnabled === 'on') {
                if (settings.honeypotApiKey) {
                    honeypot = Honeypot(settings.honeypotApiKey)
                } else {
                    winston.error('[plugins/' + pluginData.nbbId + '] Honeypot API Key not set!');
                }
            }

			if (settings.recaptchaEnabled === 'on') {
				if (settings.recaptchaPublicKey && settings.recaptchaPrivateKey ) {
					var recaptchaLanguages = {'en': 1, 'nl': 1, 'fr': 1, 'de': 1, 'pt': 1, 'ru': 1, 'es': 1, 'tr': 1},
						lang = (Meta.config.defaultLang || 'en').toLowerCase();

					recaptchaArgs = {
						publicKey: settings.recaptchaPublicKey,
						targetId: pluginData.nbbId + '-recaptcha-target',
						options: {
							theme: settings.recaptchaTheme || 'clean',
							lang: recaptchaLanguages[lang] ? lang : 'en',
							tabindex: settings.recaptchaTabindex || 0
						}
					};
				}
			} else {
				recaptchaArgs = null;
			}

			pluginSettings = settings;
        } else {
            winston.warn('[plugins/' + pluginData.nbbId + '] Settings not set or could not be retrived!');
        }
    });

    app.get('/admin/plugins/' + pluginData.nbbId, middleware.admin.buildHeader, render);
    app.get('/api/admin/plugins/' + pluginData.nbbId, render);
};

Plugin.addCaptcha = function(req, res, templateData, callback) {
	if (recaptchaArgs) {

		templateData.captcha = {
			label: 'Captcha',
			html: ''
				+ '

'
				+ ''
		}
	}
	callback(null, req, res, templateData);
};

Plugin.checkReply = function(data, callback) {
    // http://akismet.com/development/api/#comment-check
    if (akismet && data.req) {
        akismet.checkSpam({
            user_ip: data.req.ip,
            user_agent: data.req.headers['user-agent'],
            blog: data.req.protocol + '://' + data.req.host,
            permalink: data.req.path,
            comment_content: data.content,
            comment_author: data.username
        }, function(err, spam) {
            if (err) {
                winston.error(err);
            }
            if(spam)  {
                winston.warn('[plugins/' + pluginData.nbbId + '] Post "' + data.content + '" by uid: ' + data.username + '@' + data.req.ip + ' was flagged as spam and rejected.');
                callback(new Error('Post content was flagged as spam by Akismet.com'), data);
            } else {
                callback(null, data);
            }
        });
    } else {
        callback(null, data);
    }
};

Plugin.checkRegister = function(req, res, userData, callback) {
	async.parallel([
		function(next) {
			Plugin._honeypotCheck(req, res, userData, next);
		},
		function(next) {
			Plugin._recaptchaCheck(req, res, userData, next)
		}
	], function(err, results) {
			callback(err, userData);
	});
};

Plugin._honeypotCheck = function(req, res, userData, next) {
	if (honeypot && req && req.ip) {
		honeypot.query(req.ip, function (err, results) {
			if (err) {
				winston.error(err);
				next(null, userData);
			} else {
				if (results && results.found && results.type) {
					if (results.type.spammer || results.type.suspicious) {
						var message = userData.username + ' | ' + userData.email + ' was detected as ' +  (results.type.spammer ? 'spammer' : 'suspicious');

						winston.warn('[plugins/' + pluginData.nbbId + '] ' + message + ' and was denied registration.');
						next({source: 'honeypot', message: message}, userData);
					} else {
						next(null, userData);
					}
				} else {
					winston.warn('[plugins/' + pluginData.nbbId + '] username:' + userData.username + ' ip:' + req.ip + ' was not found in Honeypot database');
					next(null, userData);
				}
			}
		});
	} else {
		next(null, userData);
	}
};

Plugin._recaptchaCheck = function(req, res, userData, next) {
	if (recaptchaArgs && req && req.ip && req.body) {

		simpleRecaptcha(
			pluginSettings.recaptchaPrivateKey,
			req.ip,
			req.body.recaptcha_challenge_field,
			req.body.recaptcha_response_field,
			function(err) {
				if (err) {
					var message = err.Error || 'Wrong Captcha';
					winston.warn('[plugins/' + pluginData.nbbId + '] ' + message);
					next({source: 'recaptcha', message: message}, userData);
				} else {
					next(null, userData);
				}
			}
		);
	} else {
		next(null, userData);
	}
};

Plugin.admin = {
    menu: function(custom_header, callback) {
        custom_header.plugins.push({
            "route": '/plugins/' + pluginData.nbbId,
            "icon": pluginData.faIcon,
            "name": pluginData.name
        });

        callback(null, custom_header);
    }
};

module.exports = Plugin;
<commit_msg>match checkRegister signature<commit_after>var	Akismet = require('akismet'),
    Honeypot = require('project-honeypot'),
	simpleRecaptcha = require('simple-recaptcha'),
    pluginData = require('./plugin.json'),
    winston = module.parent.require('winston'),
    nconf = module.parent.require('nconf'),
    async = module.parent.require('async'),
    Meta = module.parent.require('./meta'),
    akismet, honeypot, recaptchaArgs, pluginSettings,
	Plugin = {};

pluginData.nbbId = pluginData.id.replace(/nodebb-plugin-/, '');

var util = {
		keys: function(obj, props, value) {
			if(props == null || obj == null)
				return undefined;

			var i = props.indexOf(".");
			if( i == -1 ) {
				if(value !== undefined)
					obj[props] = value;
				return obj[props];
			}
			var prop = props.slice(0, i),
				newProps = props.slice(i + 1);

			if(props !== undefined && !(obj[prop] instanceof Object) )
				obj[prop] = {};

			return util.keys(obj[prop], newProps, value);
		}
};

Plugin.load = function(app, middleware, controllers) {

    var render = function(req, res, next) {
        res.render('admin/plugins/' + pluginData.nbbId, pluginData || {});
    };

    Meta.settings.get(pluginData.nbbId, function(err, settings) {
		if (!err && settings) {
            if (settings.akismetEnabled === 'on') {
                if (settings.akismetApiKey) {
                    akismet = require('akismet').client({blog: nconf.get('base_url'), apiKey: settings.akismetApiKey});
                    akismet.verifyKey(function(err, verified) {
                        if (!verified) {
                            winston.error('[plugins/' + pluginData.nbbId + '] Unable to verify Akismet API key.');
                            akismet = null;
                        }
                    });
                } else {
                    winston.error('[plugins/' + pluginData.nbbId + '] Akismet API Key not set!');
                }
            }

            if (settings.honeypotEnabled === 'on') {
                if (settings.honeypotApiKey) {
                    honeypot = Honeypot(settings.honeypotApiKey)
                } else {
                    winston.error('[plugins/' + pluginData.nbbId + '] Honeypot API Key not set!');
                }
            }

			if (settings.recaptchaEnabled === 'on') {
				if (settings.recaptchaPublicKey && settings.recaptchaPrivateKey ) {
					var recaptchaLanguages = {'en': 1, 'nl': 1, 'fr': 1, 'de': 1, 'pt': 1, 'ru': 1, 'es': 1, 'tr': 1},
						lang = (Meta.config.defaultLang || 'en').toLowerCase();

					recaptchaArgs = {
						publicKey: settings.recaptchaPublicKey,
						targetId: pluginData.nbbId + '-recaptcha-target',
						options: {
							theme: settings.recaptchaTheme || 'clean',
							lang: recaptchaLanguages[lang] ? lang : 'en',
							tabindex: settings.recaptchaTabindex || 0
						}
					};
				}
			} else {
				recaptchaArgs = null;
			}

			pluginSettings = settings;
        } else {
            winston.warn('[plugins/' + pluginData.nbbId + '] Settings not set or could not be retrived!');
        }
    });

    app.get('/admin/plugins/' + pluginData.nbbId, middleware.admin.buildHeader, render);
    app.get('/api/admin/plugins/' + pluginData.nbbId, render);
};

Plugin.addCaptcha = function(req, res, templateData, callback) {
	if (recaptchaArgs) {

		templateData.captcha = {
			label: 'Captcha',
			html: ''
				+ '

'
				+ ''
		}
	}
	callback(null, req, res, templateData);
};

Plugin.checkReply = function(data, callback) {
    // http://akismet.com/development/api/#comment-check
    if (akismet && data.req) {
        akismet.checkSpam({
            user_ip: data.req.ip,
            user_agent: data.req.headers['user-agent'],
            blog: data.req.protocol + '://' + data.req.host,
            permalink: data.req.path,
            comment_content: data.content,
            comment_author: data.username
        }, function(err, spam) {
            if (err) {
                winston.error(err);
            }
            if(spam)  {
                winston.warn('[plugins/' + pluginData.nbbId + '] Post "' + data.content + '" by uid: ' + data.username + '@' + data.req.ip + ' was flagged as spam and rejected.');
                callback(new Error('Post content was flagged as spam by Akismet.com'), data);
            } else {
                callback(null, data);
            }
        });
    } else {
        callback(null, data);
    }
};

Plugin.checkRegister = function(req, res, userData, callback) {
	async.parallel([
		function(next) {
			Plugin._honeypotCheck(req, res, userData, next);
		},
		function(next) {
			Plugin._recaptchaCheck(req, res, userData, next)
		}
	], function(err, results) {
			callback(err, req, res, userData);
	});
};

Plugin._honeypotCheck = function(req, res, userData, next) {
	if (honeypot && req && req.ip) {
		honeypot.query(req.ip, function (err, results) {
			if (err) {
				winston.error(err);
				next(null, userData);
			} else {
				if (results && results.found && results.type) {
					if (results.type.spammer || results.type.suspicious) {
						var message = userData.username + ' | ' + userData.email + ' was detected as ' +  (results.type.spammer ? 'spammer' : 'suspicious');

						winston.warn('[plugins/' + pluginData.nbbId + '] ' + message + ' and was denied registration.');
						next({source: 'honeypot', message: message}, userData);
					} else {
						next(null, userData);
					}
				} else {
					winston.warn('[plugins/' + pluginData.nbbId + '] username:' + userData.username + ' ip:' + req.ip + ' was not found in Honeypot database');
					next(null, userData);
				}
			}
		});
	} else {
		next(null, userData);
	}
};

Plugin._recaptchaCheck = function(req, res, userData, next) {
	if (recaptchaArgs && req && req.ip && req.body) {

		simpleRecaptcha(
			pluginSettings.recaptchaPrivateKey,
			req.ip,
			req.body.recaptcha_challenge_field,
			req.body.recaptcha_response_field,
			function(err) {
				if (err) {
					var message = err.Error || 'Wrong Captcha';
					winston.warn('[plugins/' + pluginData.nbbId + '] ' + message);
					next({source: 'recaptcha', message: message}, userData);
				} else {
					next(null, userData);
				}
			}
		);
	} else {
		next(null, userData);
	}
};

Plugin.admin = {
    menu: function(custom_header, callback) {
        custom_header.plugins.push({
            "route": '/plugins/' + pluginData.nbbId,
            "icon": pluginData.faIcon,
            "name": pluginData.name
        });

        callback(null, custom_header);
    }
};

module.exports = Plugin;
<|endoftext|>


<commit_before>(function () {
    angular.module('hippo.theme.example', ['hippo.theme'])

        /**
         * @ngdoc object
         * @name hippo.theme.example:SelectBoxCtrl
         *
         * @description
         * Controller for the Select Box example.
         */
        .controller('SelectBoxCtrl', ['$scope', function ($scope) {
            console.log('Select box ctrl init!');

            $scope.selectedValues = [];
            $scope.options = [
                { "id": 1, "name": 'Option 1' },
                { "id": 2, "name": 'Option 2' },
                { "id": 3, "name": 'Option 3' },
                { "id": 4, "name": 'Option 4' },
                { "id": 5, "name": 'Option 5' }
            ];
        }]);
})();<commit_msg>Example to change the placeholder<commit_after>(function () {
    angular.module('hippo.theme.example', ['hippo.theme'])

        /**
         * @ngdoc object
         * @name hippo.theme.example:SelectBoxCtrl
         *
         * @description
         * Controller for the Select Box example.
         */
        .controller('SelectBoxCtrl', ['$scope', function ($scope) {
            console.log('Select box ctrl init!');

            $scope.selectedValues = [];
            $scope.options = [
                { "id": 1, "name": 'Option 1' },
                { "id": 2, "name": 'Option 2' },
                { "id": 3, "name": 'Option 3' },
                { "id": 4, "name": 'Option 4' },
                { "id": 5, "name": 'Option 5' }
            ];
            $scope.placeholder = "Choose an option";
        }]);
})();<|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 * @ignore
 */
const LoaderPlugin = require('../LoaderPlugin.js').LoaderPlugin;
const PathesConfiguration = require('../../configuration/PathesConfiguration.js').PathesConfiguration;
const GlobalConfiguration = require('../../configuration/GlobalConfiguration.js').GlobalConfiguration;
const Entity = require('../../entity/Entity.js').Entity;
const Site = require('../../site/Site.js').Site;
const assertParameter = require('../../../utils/assert.js').assertParameter;
const co = require('co');
const fs = require('co-fs-extra');

/**
 * Reads the entity specific configuration file
 */
class PackagePlugin extends LoaderPlugin
{
    /**
     * @param {model.configuration.PathesConfiguration} pathesConfiguration
     * @param {model.configuration.GlobalConfiguration} globalConfiguration
     * @param {object|undefined} options
     */
    constructor(pathesConfiguration, globalConfiguration, settingsRepository, options)
    {
        super();

        //Check params
        assertParameter(this, 'pathesConfiguration', pathesConfiguration, true, PathesConfiguration);
        assertParameter(this, 'globalConfiguration', globalConfiguration, true, GlobalConfiguration);

        // Assign options
        const opts = options || {};
        this._pathesConfiguration = pathesConfiguration;
        this._globalConfiguration = globalConfiguration;
        this._filename = opts.filename || '/entity.json';
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': [PathesConfiguration, GlobalConfiguration, 'model.loader.documentation/PackagePlugin.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.loader.documentation/PackagePlugin';
    }

    /**
     * @property {string}
     */
    get filename()
    {
        return this._filename;
    }

    /**
     * @property {model.configuration.PathesConfiguration}
     */
    get pathesConfiguration()
    {
        return this._pathesConfiguration;
    }

    /**
     * @property {model.configuration.GlobalConfiguration}
     */
    get globalConfiguration()
    {
        return this._globalConfiguration;
    }

    /**
     * @property {model.setting.SettingsRepository}
     */
    get settingsRepository()
    {
        return this._settingsRepository;
    }

    /**
     * @param {String} content
     */
    parse(content)
    {
        const result = JSON.parse(content);

        // Enhance example settings
        if (result && result.examples && result.examples.settings)
        {
            for (const setting of result.examples.settings)
            {
                if (setting.type == 'language')
                {
                    setting.name = setting.name || 'language';
                    setting.label = setting.label || 'Language';
                    setting.type = 'select';
                    setting.items = this.globalConfiguration.get('languages.list', []);
                    setting.default = this.globalConfiguration.get('languages.active', 'en_US');
                }
                if (setting.type == 'locale')
                {
                    setting.name = setting.name || 'locale';
                    setting.label = setting.label || 'Locale';
                    setting.type = 'select';
                    setting.items = this.globalConfiguration.get('languages.list', []);
                    setting.default = this.globalConfiguration.get('languages.active', 'en_US');
                }
                /*
                if (setting.type == 'settings')
                {
                    setting.name = setting.name || 'settings';
                    setting.label = setting.label || setting.name;
                    setting.type = 'autocomplete';
                    setting.items = waitForPromise(this.settingsRepository.getByName(setting.key));
                }
                */
            }
        }

        return Promise.resolve(result);
    }

    /**
     * @param {model.entity.Entity} item
     * @param {model.site.Site} site
     */
    executeFor(item, site)
    {
        // Guards
        assertParameter(this, 'item', item, true, [Entity, Site]);
        assertParameter(this, 'site', site, false, Site);

        const scope = this;
        const promise = co(function*()
        {
            // Get filename
            let filename;
            if (!site)
            {
                filename = yield scope._pathesConfiguration.resolve(item, scope.filename);
            }
            else
            {
                filename = yield scope._pathesConfiguration.resolveEntityForSite(item, site, scope.filename);
            }

            // Check file
            const exists = yield fs.exists(filename);
            /* istanbul ignore next */
            if (!exists)
            {
                scope.logger.debug('Could not find file', filename);
                return false;
            }

            // Read it
            const content = yield fs.readFile(filename);
            /* istanbul ignore next */
            if (!content)
            {
                scope.logger.warn('Could not read file', filename);
                return false;
            }

            // Parse it
            let data = false;
            try
            {
                data = yield scope.parse(content);
            }
            catch(e)
            {
                /* istanbul ignore next */
                scope.logger.error('Could not parse file', filename, e);
                /* istanbul ignore next */
                return false;
            }

            // Add data to item
            if (data)
            {
                // Get site
                let s = site;
                if (!s && item.id && item.id.site)
                {
                    s = item.id.site;
                }

                // Add namespaced
                if (s)
                {
                    const namespacedData = {};
                    namespacedData[s.name.toLowerCase()] = data;
                    item.properties.load(namespacedData);
                }
                // Add straight
                else
                {
                    item.properties.load(data);
                }
            }

            return true;
        });
        return promise;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.PackagePlugin = PackagePlugin;
<commit_msg>style(model.loader.documentation.PackagePlugin): remove unused code<commit_after>'use strict';

/**
 * Requirements
 * @ignore
 */
const LoaderPlugin = require('../LoaderPlugin.js').LoaderPlugin;
const PathesConfiguration = require('../../configuration/PathesConfiguration.js').PathesConfiguration;
const GlobalConfiguration = require('../../configuration/GlobalConfiguration.js').GlobalConfiguration;
const Entity = require('../../entity/Entity.js').Entity;
const Site = require('../../site/Site.js').Site;
const assertParameter = require('../../../utils/assert.js').assertParameter;
const co = require('co');
const fs = require('co-fs-extra');

/**
 * Reads the entity specific configuration file
 */
class PackagePlugin extends LoaderPlugin
{
    /**
     * @param {model.configuration.PathesConfiguration} pathesConfiguration
     * @param {model.configuration.GlobalConfiguration} globalConfiguration
     * @param {object|undefined} options
     */
    constructor(pathesConfiguration, globalConfiguration, settingsRepository, options)
    {
        super();

        //Check params
        assertParameter(this, 'pathesConfiguration', pathesConfiguration, true, PathesConfiguration);
        assertParameter(this, 'globalConfiguration', globalConfiguration, true, GlobalConfiguration);

        // Assign options
        const opts = options || {};
        this._pathesConfiguration = pathesConfiguration;
        this._globalConfiguration = globalConfiguration;
        this._filename = opts.filename || '/entity.json';
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': [PathesConfiguration, GlobalConfiguration, 'model.loader.documentation/PackagePlugin.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.loader.documentation/PackagePlugin';
    }

    /**
     * @property {string}
     */
    get filename()
    {
        return this._filename;
    }

    /**
     * @property {model.configuration.PathesConfiguration}
     */
    get pathesConfiguration()
    {
        return this._pathesConfiguration;
    }

    /**
     * @property {model.configuration.GlobalConfiguration}
     */
    get globalConfiguration()
    {
        return this._globalConfiguration;
    }

    /**
     * @property {model.setting.SettingsRepository}
     */
    get settingsRepository()
    {
        return this._settingsRepository;
    }

    /**
     * @param {String} content
     */
    parse(content)
    {
        const result = JSON.parse(content);

        // Enhance example settings
        if (result && result.examples && result.examples.settings)
        {
            for (const setting of result.examples.settings)
            {
                if (setting.type == 'language')
                {
                    setting.name = setting.name || 'language';
                    setting.label = setting.label || 'Language';
                    setting.type = 'select';
                    setting.items = this.globalConfiguration.get('languages.list', []);
                    setting.default = this.globalConfiguration.get('languages.active', 'en_US');
                }
                if (setting.type == 'locale')
                {
                    setting.name = setting.name || 'locale';
                    setting.label = setting.label || 'Locale';
                    setting.type = 'select';
                    setting.items = this.globalConfiguration.get('languages.list', []);
                    setting.default = this.globalConfiguration.get('languages.active', 'en_US');
                }
            }
        }

        return Promise.resolve(result);
    }

    /**
     * @param {model.entity.Entity} item
     * @param {model.site.Site} site
     */
    executeFor(item, site)
    {
        // Guards
        assertParameter(this, 'item', item, true, [Entity, Site]);
        assertParameter(this, 'site', site, false, Site);

        const scope = this;
        const promise = co(function*()
        {
            // Get filename
            let filename;
            if (!site)
            {
                filename = yield scope._pathesConfiguration.resolve(item, scope.filename);
            }
            else
            {
                filename = yield scope._pathesConfiguration.resolveEntityForSite(item, site, scope.filename);
            }

            // Check file
            const exists = yield fs.exists(filename);
            /* istanbul ignore next */
            if (!exists)
            {
                scope.logger.debug('Could not find file', filename);
                return false;
            }

            // Read it
            const content = yield fs.readFile(filename);
            /* istanbul ignore next */
            if (!content)
            {
                scope.logger.warn('Could not read file', filename);
                return false;
            }

            // Parse it
            let data = false;
            try
            {
                data = yield scope.parse(content);
            }
            catch(e)
            {
                /* istanbul ignore next */
                scope.logger.error('Could not parse file', filename, e);
                /* istanbul ignore next */
                return false;
            }

            // Add data to item
            if (data)
            {
                // Get site
                let s = site;
                if (!s && item.id && item.id.site)
                {
                    s = item.id.site;
                }

                // Add namespaced
                if (s)
                {
                    const namespacedData = {};
                    namespacedData[s.name.toLowerCase()] = data;
                    item.properties.load(namespacedData);
                }
                // Add straight
                else
                {
                    item.properties.load(data);
                }
            }

            return true;
        });
        return promise;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.PackagePlugin = PackagePlugin;
<|endoftext|>