<|start_filename|>server/mt/kibana/defaultIndexPattern/_exists.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {find} from '../savedObjectsToolkit';

export async function defaultLogsIndexExists(server, userObj) {
  const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`) + '*';
  server.log(['status', 'debug', 'keystone'],
    `Checking if default logs-index pattern for ${userObj.project.id} exists...`);

  return await patternExists(server, pattern);
}

export async function defaultEventsIndexExists(server, userObj) {
  const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`) + '*';
  server.log(['status', 'debug', 'keystone'],
    `Checking if default events-index pattern for ${userObj.project.id} exists...`);

  return await patternExists(pattern);
}

async function patternExists(pattern) {
  const params = {
    type: 'index-pattern',
    fields: ['title'],
  };

  return find(params)
    .then((response) => {
      return response.saved_objects.some(el => el.attributes.title === pattern);
    }).catch((err) => {
      throw new Error(`Checking if ${pattern} exists failed, error is ${err}`);
    });
}


<|start_filename|>server/mt/routing/routes/find.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {findWithMeta} from '../../kibana/savedObjectsToolkit';
import {filterResponse} from '../_utils';
import {SESSION_USER_KEY} from '../../../const';
import getAllowedPatterns from '../../kibana/defaultIndexPattern/_get_allowed_patterns';

export default function (server, method, path) {
  return {
    method: method,
    path: path,
    config: {
      auth: 'keystone-session',
    },
    handler: handler
  };

  async function handler(request, reply) {
    const userObj = request.yar.get(SESSION_USER_KEY);
    const project = userObj.project.id;
    const patterns = getAllowedPatterns(server, project);
    const params = request.query;

    if (params.fields && !(params.fields instanceof Array)) {
      params.fields = [params.fields];
    }
    return findWithMeta(server, params)
      .then((response) => {
        return filterResponse(response, patterns, server);
      })
      .then((response) => {
        response.total = response.saved_objects.length;
        return response;
      })
      .catch((e) => {
        throw new Error(`Find route failed, error: ${e}`);
      });
  }
}


<|start_filename|>server/mt/index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

export default {
  bind: (server) => {
    server.log(['status', 'info', 'keystone'], 'Registering keystone-auth schema');
    return Promise.all([
      bindAuthScheme(server),
      bindRouting(server)
    ]);
  }
};

//I suppose the handler that would prevent user from deleting his default index pattern should be implemented as well
function bindRouting(server) {
  return require('./routing')(server)
    .then((route) => {
      route(server, ['GET', 'POST'], '/{paths*}');
      route(server, ['GET', 'POST'], '/{search_pattern}/_search');
      route(server, 'GET', '/api/saved_objects/_find');
      route(server, 'GET', '/api/kibana/management/saved_objects/_find');
      route(server, 'POST', '/api/saved_objects/_bulk_get');
    });
}

function bindAuthScheme(server) {
  return Promise.all([
    server.auth.scheme(
      'keystone-token',
      require('./auth/scheme')
    ),
    server.auth.strategy(
      'keystone-session',
      'keystone-token',
      require('./auth/strategy')(server)
    )
  ]);
}

//bindExt is removed due to verify being a part of authentication scheme and the other one was no longer relevant


<|start_filename|>server/session/index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
import yarCookie from '@hapi/yar';
import multiTenancy from '../mt';
import {initClients} from '../mt/kibana/savedObjectsToolkit';

export default (server) => {
  const config = server.config();
  return {
    start: async () => {
      await server.register({
        plugin: yarCookie,
        options: {
          maxCookieSize: 4096,
          name: config.get('monasca-kibana-plugin.cookie.name'),
          storeBlank: false,
          cache: {
            expiresIn: config.get('monasca-kibana-plugin.cookie.expiresIn')
          },
          cookieOptions: {
            password: config.get('monasca-kibana-plugin.cookie.password'),
            isSecure: config.get('monasca-kibana-plugin.cookie.isSecure'),
            ignoreErrors: config.get('monasca-kibana-plugin.cookie.ignoreErrors'),
            clearInvalid: false
          }
        }
      }).catch((err) => {
        throw new Error(err);
      });
      server.log(['status', 'info', 'keystone'], 'Session registered');
      initClients(server);
      await multiTenancy.bind(server);

    }
  };
};


<|start_filename|>server/mt/kibana/defaultIndexPattern/index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {createEventsIndexPattern, createLogsIndexPattern} from './_create';
import {defaultEventsIndexExists, defaultLogsIndexExists} from './_exists';
import {deleteDefaultEventsIndex, deleteDefaultLogsIndex} from './_delete';
import {updateEventsConfig, updateLogsConfig} from './_set_default_index_pattern';

export default (server, userObj) => {
  return () => {
    const project = userObj.project.id;
    return defaultLogsIndexExists(server, userObj)
      .then((logsExists) => {
        if (server.config().get('monasca-kibana-plugin.logs')) {
          server.log(['status', 'info', 'keystone'],
            'Default logs-index pattern is enabled in kibana config file');
          if (!logsExists) {
            server.log(['status', 'warning', 'keystone'],
              `Default logs-index pattern for ${project} does not exist`);
            return createLogsIndexPattern(server, userObj)
              .then(() => {
                return updateLogsConfig(server, userObj);
              });
          } else {
            server.log(['status', 'info', 'keystone'],
              `Default logs-index pattern for ${project} already exists`);
          }
        } else {
          server.log(['status', 'info', 'keystone'],
            'Default logs-index pattern is disabled in kibana config file');
          if (logsExists) {
            server.log(['status', 'warning', 'keystone'],
              `Default logs-index pattern for ${project} exists, but it should not`);
            return deleteDefaultLogsIndex(server, userObj);
          } else {
            server.log(['status', 'info', 'keystone'],
              `Default logs-index pattern for ${project} does not exist`);
          }
        }
      })
      .then(() => {
        defaultEventsIndexExists(server, userObj)
          .then((eventsExists) => {
            if (server.config().get('monasca-kibana-plugin.events')) {
              server.log(['status', 'info', 'keystone'],
                'Default events-index pattern is enabled in kibana config file');
              if (!eventsExists) {
                server.log(['status', 'warning', 'keystone'],
                  `Default events-index pattern for ${project} does not exist`);
                return createEventsIndexPattern(server, userObj)
                  .then(() => {
                    return updateEventsConfig(server, userObj);
                  });
              } else {
                server.log(['status', 'info', 'keystone'],
                  `Default events-index pattern for ${project} already exists`);
              }
            } else {
              server.log(['status', 'info', 'keystone'],
                'Default events-index pattern is disabled in kibana config file');
              if (eventsExists) {
                server.log(['status', 'warning', 'keystone'],
                  `Default events-index pattern for ${project} exists, but it should not`);
                return deleteDefaultEventsIndex(server, userObj);
              } else {
                server.log(['status', 'info', 'keystone'],
                  `Default events-index pattern for ${project} does not exist`);
              }
            }
          });
      });
  };
};


<|start_filename|>server/mt/kibana/defaultIndexPattern/_create.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {createSavedObject} from '../savedObjectsToolkit';

export async function createLogsIndexPattern(server, userObj) {
  server.log(['status', 'info', 'keystone'],
    `Creating default logs-index pattern for ${userObj.project.id}`);
  const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`) + '*';

  return await createIndexPattern(server, pattern);
}

export async function createEventsIndexPattern(server, userObj) {
  server.log(['status', 'info', 'keystone'],
    `Creating default events-index pattern for ${userObj.project.id}`);
  const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`) + '*';

  return await createIndexPattern(server, pattern);
}

async function createIndexPattern(server, pattern) {
  let timeFieldName = server.config().get('monasca-kibana-plugin.defaultTimeField');
  const type = 'index-pattern';
  const params = {
    title: pattern,
    timeFieldName: timeFieldName,
  };
  const options = {
    id: pattern,
    overwrite: true
  };

  return createSavedObject(type, params, options)
    .then(() => {
      server.log(['status', 'info', 'keystone', 'create'], `Created ${type} ${pattern}`);
    });
}


<|start_filename|>server/binding/index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import TokensApi from 'keystone-v3-client/lib/keystone/tokens';
import UsersApi from 'keystone-v3-client/lib/keystone/users';

import util from '../util';

module.exports = function binding(server) {
  const config = server.config();
  const url = util.keystoneUrl(config);

  server.log(['keystone', 'binding', 'debug'], `keystone url is ${url}`);

  const keystoneCfg = {
    url: url
  };

  return {
    start: () => {
      server.expose('tokens', new TokensApi(keystoneCfg));
      server.expose('users', new UsersApi(keystoneCfg));
    }
  };

};


<|start_filename|>server/mt/kibana/savedObjectsToolkit/index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

let savedObjectsClient;

export function initClients(server) {
  const elasticsearchClient = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser;
  const {SavedObjectsClient, getSavedObjectsRepository} = server.savedObjects;
  const internalRepository = getSavedObjectsRepository(elasticsearchClient);

  savedObjectsClient = new SavedObjectsClient(internalRepository);
}

export function findWithMeta(server, params) {
  const version = server.config().get('pkg.version');

  params.fields ? params.fields.push('title') : params.fields = ['title'];
  params.fields.push('visState');

  return find(params)
    .then((response) => {
      response.saved_objects.forEach(el => _createMetaForSavedObject(el, version));
      return response;
    })
    .catch((err) => {
      throw new Error(err);
    });
}

export async function bulkGetSavedObjects(params) {
  return await savedObjectsClient.bulkGet(params)
    .catch((err) => {
      throw new Error(err);
    });
}


function _createMetaForSavedObject(object, version) {
  const inAppPrefix = '/app/kibana#';
  const managementPrefix = '/management/kibana';
  switch (object.type) {
    case 'config':
      object.meta = {
        title: `Advanced Settings [${version}]`,
        inAppUrl: {
          path: `${inAppPrefix}${managementPrefix}/settings`,
          uiCapabilitiesPath: 'advancedSettings.show'
        }
      };
      break;
    case 'index-pattern':
      object.meta = {
        icon: 'indexPatternApp',
        title: object.attributes.title,
        editUrl: `${managementPrefix}/index_patterns/${object.title}`,
        inAppUrl: {
          path: `${inAppPrefix}${managementPrefix}/index_patterns/${object.id}`,
          uiCapabilitiesPath: 'management.kibana.index_patterns'
        }
      };
      break;
    case 'search':
      object.meta = {
        icon: 'search',
        title: object.attributes.title,
        editUrl: `${managementPrefix}/objects/savedSearches/${object.id}`,
        inAppUrl: {
          path: `${inAppPrefix}/discover/${object.id}`,
          uiCapabilitiesPath: 'discover.show'
        }
      };
      break;
    case 'visualization':
      object.meta = {
        icon: 'visualizeApp',
        title: object.attributes.title,
        editUrl: `${managementPrefix}/objects/savedVisualizations/${object.id}`,
        inAppUrl: {
          path: `${inAppPrefix}/visualize/edit/${object.id}`,
          uiCapabilitiesPath: 'visualize.show'
        }
      };
      break;
    case 'dashboard':
      object.meta = {
        icon: 'dashboardApp',
        title: object.attributes.title,
        editUrl: `${managementPrefix}/objects/savedDashboards/${object.id}`,
        inAppUrl: {
          path: `${inAppPrefix}/dashboard/${object.id}`,
          uiCapabilitiesPath: 'dashboard.show'
        }
      };
      break;
    default:
      throw new Error(`Unknown saved object type ${object.type}`);
  }
  return object;
}

export async function createSavedObject(type, options, params) {
  return await savedObjectsClient.create(type, options, params)
    .catch((err) => {
      throw new Error(`Unable to create ${type} ${options}, error is ${err}`);
    });
}

export async function find(params) {
  return await savedObjectsClient.find(params)
    .catch((err) => {
      throw new Error(`Can't perform search ${params}, error is: ${err}`);
    });
}

export async function updateConfig(type, version, changes) {
  return await savedObjectsClient.update(type, version, changes)
    .catch((err) => {
      throw new Error(`Can't update ${type} with ${changes}, error is: ${err}`);
    });
}

export async function deleteSavedObject(type, id) {
  return await savedObjectsClient.delete(type, id)
    .catch((err) => {
      throw new Error(`Can't delete ${type} ${id}, error is: ${err}`);
    });
}


<|start_filename|>server/mt/routing/routes/search.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import Wreck from '@hapi/wreck';

import {getOpts, parsePayload} from '../_utils';
import mapUri from '../_map_uri';

export default function (server, method, path) {

  return {
    method: method,
    path: path,
    config: {
      auth: 'keystone-session',
    },
    handler: handler
  };

  async function handler(request, reply) {
    const url = mapUri(server, request);
    const payload = parsePayload(request);
    const opts = getOpts(server, request, url, payload);

    try {
      const rawResponse = await Wreck.request(request.method, url, opts);
      const body = await Wreck.read(rawResponse, opts);
      let response = JSON.parse(body.toString());

      return reply.response(response).code(rawResponse.statusCode)
        .passThrough(!!opts.passThrough);
    } catch (e) {
      throw new Error(e);
    }
  }
}


<|start_filename|>server/mt/routing/routes/bulk_get.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {bulkGetSavedObjects} from '../../kibana/savedObjectsToolkit';
import getAllowedPatterns from '../../kibana/defaultIndexPattern/_get_allowed_patterns';
import {SESSION_USER_KEY} from '../../../const';

export default function (server, method, path) {
  return {
    method: method,
    path: path,
    config: {
      auth: 'keystone-session',
    },
    handler: handler
  };

  async function handler(request, reply) {
    const userObj = request.yar.get(SESSION_USER_KEY);
    const patterns = getAllowedPatterns(server, userObj.project.id);
    const params = request.payload;

    if (params.type === 'index-pattern') {
      if (!patterns.includes(params.id)) {
        server.log(['error', 'api-bulk-get'], `Index-pattern ${params.id} is not allowed`);
        throw new Error(`Index-pattern ${params.id} is not allowed`);
      }
    }
    return bulkGetSavedObjects(params);
  }
}


<|start_filename|>server/mt/routing/_utils.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import createAgent from './_create_agent';
import {bulkGetSavedObjects} from '../kibana/savedObjectsToolkit';

export const PREFIX = '/mt';

export function getOpts(server, request, url, payload) {

  let options = {
    headers: {},
    redirects: true,
    passThrough: true,
    xforward: true,
    search_timeout: '10s',
    localStatePassThrough: false,
    agent: createAgent(server),
  };
  let protocol = url.split(':', 1)[0];

  if (payload) {
    options.payload = JSON.stringify(payload);
  }

  if (options.passThrough) {
    options.headers = require('hoek').clone(request.headers);
    //delete options.headers.host;
    if (options.acceptEncoding === false) {
      delete options.headers['accept-encoding'];
    }
  }

  if (options.xforward &&
    request.info.remoteAddress &&
    request.info.remotePort) {

    options.headers['x-forwarded-for'] = (options.headers['x-forwarded-for'] ?
      options.headers['x-forwarded-for'] + ',' : '') + request.info.remoteAddress;
    options.headers['x-forwarded-port'] = (options.headers['x-forwarded-port'] ?
      options.headers['x-forwarded-port'] + ',' : '') + request.info.remotePort;
    options.headers['x-forwarded-proto'] = (options.headers['x-forwarded-proto'] ?
      options.headers['x-forwarded-proto'] + ',' : '') + protocol;
  }

  const contentType = request.headers['content-type'];
  if (contentType) {
    options.headers['content-type'] = contentType;
  }

  return options;
}

export function parsePayload(request) {
  let payload = request.payload;
  if (!payload || payload.length <= 0) {
    return {};
  }
  return JSON.parse(payload.toString('utf-8'));
}


function isAllowedSavedObject(element, patterns) {
  let references = [];
  switch (element.type) {
    case 'index-pattern':
      return patterns.includes(element.id);
    case 'config':
      return true;
    case 'visualization':
    case 'search':
      references = element.references.filter(element => patterns.includes(element.id));
      return references.length > 0;
    default:
      return false;
  }
}

// We have to retrieve savedObjects once again because some of the searches may not contain the references of references
// Dashboard would be allowed only if it references saved objects accessible to the user
function isAllowedDashboard(element, patterns) {

  const dashboardRefs = element.references.map((el) => {
    return {id: el.id, type: el.type};
  });

  return bulkGetSavedObjects(dashboardRefs)
    .then((response) => {
      let onlyAllowed = true;
      const references = response.saved_objects.map((el) => el.references)[0];
      references.forEach((hit) => {
        if (!patterns.includes(hit.id)) onlyAllowed = false;
      });
      return onlyAllowed;
    });
}

async function filterDashboards(object, patterns) {
  let dashboards = [];
  for (let element of object) {
    if (element.type === 'dashboard' && await isAllowedDashboard(element, patterns)) {
      dashboards.push(element);
    }
  }
  return dashboards;
}

// Dashboards have to be processed separately because it's references in saved_objects
// don't contain the references to index-pattern
// Dashboard is deemed allowed only if it contains allowed references only.
export async function filterResponse(data, patterns) {
  let savedObjects = data.saved_objects.filter(el => isAllowedSavedObject(el, patterns));
  let savedDashboards = await filterDashboards(data.saved_objects, patterns);

  data.saved_objects = savedObjects.concat(savedDashboards);
  return data;
}


<|start_filename|>index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import session from './server/session';
import binding from './server/binding';
import healthCheck from './server/healthcheck';

export default (kibana) => {
  const COOKIE_PASSWORD_SIZE = 32;

  return new kibana.Plugin({
    require: ['elasticsearch'],
    config : config,
    init   : init
  });

  function config(Joi) {

    const cookie = Joi.object({
      name        : Joi.string()
        .default('keystone'),
      password    : Joi.string()
        .min(COOKIE_PASSWORD_SIZE)
        .default(require('crypto').randomBytes(COOKIE_PASSWORD_SIZE).toString('hex')),
      isSecure    : Joi.boolean()
        .default(process.env.NODE_ENV !== 'development'),
      ignoreErrors: Joi.boolean()
        .default(true),
      expiresIn   : Joi.number()
        .positive()
        .integer()
        .default(60 * 60 * 1000) // 1 hour
    }).default();

    const deprecatedKeystone = Joi.object({
      url : Joi.string().uri({scheme: ['http', 'https']}),
      port: Joi.number(),
    })
      .tags(['deprecated'])
      .notes(['url,port settings have been deprecated in favour of auth_uri'])
      .default();

    const validKeystone = Joi.object({
      auth_uri: Joi.string().uri({scheme: ['http', 'https']})
    })
      .default();

    //Elasticsearch parameter added due to the fact that retrieving elasticsearch url via server.config() is impossible
    const elasticsearch = Joi.object({
      url: Joi.string().uri({scheme: ['http', 'https']})
    }).default();

    return Joi
      .object({
        enabled: Joi.boolean().default(true),
        events: Joi.boolean().default(false),
        logs: Joi.boolean().default(true),
        defaultTimeField: Joi.string().default('@timestamp'),
        defaultEventsTimeField: Joi.string().default('@timestamp'),
        logsIndexPrefix: Joi.string().default('logs-<project_id>'),
        eventsIndexPrefix: Joi.string().default('events-<project_id>'),
        cookie: cookie,
        elasticsearch: elasticsearch
      })
      .concat(deprecatedKeystone)
      .concat(validKeystone)
      .and('url', 'port')
      .without('auth_uri', ['url', 'port'])
      .default();
  }

  async function init(server) {
    server.log(['MKP','status', 'debug', 'keystone'], 'Initializing keystone plugin');
    binding(server).start();
    await session(server).start();
    healthCheck(this, server).start();
    server.log(['MKP','status', 'debug', 'keystone'], 'Initialized keystone plugin');
  }
};


<|start_filename|>server/mt/projects/index.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import Promise from 'bluebird';
import { pick, sortBy } from 'lodash';

import { SESSION_PROJECTS_KEY, SESSION_TOKEN_KEY } from '../../const';

export default (server, session, userObj) => {

  const usersApi = server.plugins['monasca-kibana-plugin'].users;

  return new Promise((resolve) => {
    return usersApi
      .allProjects({
        params : {
          user_id: userObj.user.id
        },
        headers: {
          'X-Auth-Token'   : session.get(SESSION_TOKEN_KEY),
          'X-Subject-Token': session.get(SESSION_TOKEN_KEY)
        }
      })
      .then((response) => {
        const data = response.data;
        const projects = data.projects;

        return sortBy(
          projects.map(
            project=>pick(project, ['id', 'name', 'description', 'domain_id'])
          ),
          'name'
        );
      })
      .then((projects) => {
        session.set(SESSION_PROJECTS_KEY, projects);
        return resolve(projects);
      });
  });

};


<|start_filename|>server/const.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

const NOW_TIME = (new Date().valueOf() / 1000);

export const SESSION_USER_KEY = `monasca-kibana-plugin-user-${NOW_TIME}`;
export const SESSION_TOKEN_KEY = `monasca-kibana-plugin-token-${NOW_TIME}`;
export const SESSION_PROJECTS_KEY = `monasca-kibana-plugin-projects-${NOW_TIME}`;
export const SESSION_TOKEN_CHANGED = `monasca-kibana-plugin-token-changed-${NOW_TIME}`;

export const TOKEN_CHANGED_VALUE = Symbol('token-changed');

export const RELOAD_MARKUP = `
<html>
<head><script src="#" type="text/javascript">window.location.reload();</script></head>
<body>reloading...</body>
</html>`;


<|start_filename|>server/mt/routing/_create_proxy.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import { PREFIX } from './_utils';

module.exports = (server, method, route) => {

  const pre = '/elasticsearch';
  const sep = route[0] === '/' ? '' : '/';
  let path;
  if (/\/api.*\//.test(route)) {
    path = `${PREFIX}${sep}${route}`;
  } else {
    path = `${PREFIX}${pre}${sep}${route}`;
  }

  server.log(['create-proxy-path'], path + '; ' + method);

  let options;

  switch (route) {
    case '/_search':
      options = require('./routes/search')(server, method, path);
      break;
    case '/{search_pattern}/_search':
      options = require('./routes/pattern_search')(server, method, path);
      break;
    case '/api/saved_objects/_bulk_get':
      options = require('./routes/bulk_get')(server, method, path);
      break;
    default:
      if (/\/api.*\/saved_objects\/_find/.test(route)) {
        options = require('./routes/find')(server, method, path);
      } else {
        options = require('./routes/default')(server, method, path);
      }
      break;
  }
  return server.route(options);
};


<|start_filename|>server/mt/kibana/defaultIndexPattern/_set_default_index_pattern.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */


import {updateConfig} from '../savedObjectsToolkit';

export async function updateLogsConfig(server, userObj) {
  const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`) + '*';

  return await updateDefaultIndexPattern(server, pattern);
}

export async function updateEventsConfig(server, userObj) {
  const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`) + '*';

  return await updateDefaultIndexPattern(server, pattern);
}

async function updateDefaultIndexPattern(server, pattern) {
  const type = 'config';
  const version = server.config().get('pkg.version');
  const changes = {
    defaultIndex: pattern
  };

  return await updateConfig(type, version, changes)
    .catch((err) => {
      server.log(['updateConfig'], `Can't set ${pattern} as default index pattern, error is: ${err}`);
    })
    .then(() => {
      server.log(['updateConfig'], `Default index pattern is ${pattern}`);
    });
}


<|start_filename|>server/mt/routing/_re_route.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {isRouted, requestPath} from '../../util';
import { PREFIX } from './_utils';

module.exports = function reRoute(server) {
  return (request, reply) => {
    const path = requestPath(request);
    if (isRouted(request)) {
      server.log(['status', 'debug', 'keystone'], `Routing ${path} onto ${PREFIX}${path}`);
      request.setUrl(`${PREFIX}${path}`);
    }
    return reply.continue;
  };
};



<|start_filename|>server/mt/kibana/defaultIndexPattern/_delete.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import {deleteSavedObject} from '../savedObjectsToolkit';

export async function deleteDefaultLogsIndex(server, userObj) {
  const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`);
  server.log(['status', 'info', 'keystone'],
    `Attempting to delete logs-index pattern ${pattern}`);

  return await deleteSavedObject('index-pattern', pattern);
}

export async function deleteDefaultEventsIndex(server, userObj) {
  const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix')
    .replace('<project_id>', `${userObj.project.id}`);
  server.log(['status', 'info', 'keystone'],
    `Attempting to delete events-index pattern ${pattern}`);

  return await deleteSavedObject('index-pattern', pattern);
}


<|start_filename|>server/mt/routing/routes/pattern_search.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

import Wreck from '@hapi/wreck';

import {SESSION_USER_KEY} from '../../../const';
import {getOpts} from '../_utils';
import getAllowedPatterns from '../../kibana/defaultIndexPattern/_get_allowed_patterns';
import mapUri from '../_map_uri';

export default function (server, method, path) {
  return {
    method: method,
    path: path,
    config: {
      auth: 'keystone-session',
    },
    handler: handler
  };

  async function handler(request, reply) {

    const session = request.yar;
    let url = mapUri(server, request).split('/');
    let project = session.get(SESSION_USER_KEY).project.id;

    let logsIndexPref = server.config().get('monasca-kibana-plugin.logsIndexPrefix');
    let eventsIndexPref = server.config().get('monasca-kibana-plugin.eventsIndexPrefix');
    logsIndexPref = logsIndexPref.replace('<project_id>', project);
    eventsIndexPref = eventsIndexPref.replace('<project_id>', project);

    server.log(['status', 'info', 'keystone'],
      `Allowing only these Index-Prefix ${logsIndexPref}, ${eventsIndexPref}`);

    url = url.join('/');

    const opts = getOpts(server, request, url, request.payload);
    const patterns = getAllowedPatterns(server, project);

    try {
      const rawResponse = await Wreck.request(request.method, url, opts);
      const body = await Wreck.read(rawResponse, opts);
      let response = JSON.parse(body.toString());

      if (response._shards.total > 0) {
        let indexHits = response.aggregations.indices.buckets;
        response.aggregations.indices.buckets =
          indexHits.filter(elem => patterns.includes(elem.key));
      }
      return reply.response(response).code(rawResponse.statusCode).passThrough(!!opts.passThrough);
    } catch (e) {
      throw new Error(e);
    }
  }
}


<|start_filename|>server/mt/kibana/defaultIndexPattern/_get_allowed_patterns.js<|end_filename|>
/*
 * Copyright 2020 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

export default function (server, project) {
  const logsEnabled = server.config().get('monasca-kibana-plugin.logs');
  const eventsEnabled = server.config().get('monasca-kibana-plugin.events');

  let output = [];
  if (logsEnabled) {
    const pattern = server.config().get('monasca-kibana-plugin.logsIndexPrefix')
      .replace('<project_id>', `${project}`) + '*';
    output += pattern;
  }
  if (eventsEnabled) {
    const pattern = server.config().get('monasca-kibana-plugin.eventsIndexPrefix')
      .replace('<project_id>', `${project}`) + '*';
    output += pattern;
  }
  return output;
}
