Navigation

Database User View Tutorial

Estimated time to complete: ~15 minutes

Introduction

This tutorial demonstrates a Compass plugin which displays the users of a database and their respective roles.

Once complete, your plugin will look similar to the following:

../../_images/user-tutorial-complete.png

Prerequisites

The following are required to begin building MongoDB Compass plugins:

  • MongoDB Compass (version 1.11 or greater)
  • Node Version Manager (NVM)
  • NodeJS
  • Khaos

The following procedure outlines how to install these dependencies:

  1. Install the latest version of MongoDB Compass for your operating system from the downloads page.

  2. Install the Node Version Manager (NVM):

    For MacOS and Linux operating systems:

    Follow the installation instructions at https://github.com/creationix/nvm#install-script.

    For Windows operating systems:
    1. Download the nvm-setup.zip file from https://github.com/coreybutler/nvm-windows/releases.
    2. Decompress the downloaded .zip file and run nvm-setup.exe.
  3. Install NodeJS via NVM:

    nvm install stable
    
  4. Install the Khaos templating engine:

    npm install -g khaos
    
  5. Create the MongoDB Compass plugins directory. Compass looks for plugins in this directory:

    MongoDB Compass
    mkdir -p ~/.mongodb/compass/plugins
    
    MongoDB Compass Community Edition
    mkdir -p ~/.mongodb/compass-community/plugins
    

Creating the Plugin

Run the following commands to create an empty plugin called users.

MongoDB Compass
cd ~/.mongodb/compass/plugins
khaos create mongodb-js/compass-plugin ./users
MongoDB Compass Community Edition
cd ~/.mongodb/compass-community/plugins
khaos create mongodb-js/compass-plugin ./users

When prompted, enter the following values:

Field Description
Name users
Description Displays database users and their respective roles.
Role Database.Tab

This plugin is uses the Database.Tab role, meaning it exists in its own tab in the MongoDB Compass database view.

Finally, run the following command to install the plugin’s dependencies:

cd users && npm install

Creating the Store

A store is responsible for storing and maintaining the state of the React/Flux application architecture used by MongoDB Compass plugins. The store responds to events and actions, resulting in state changes which are then reflected by the component’s view.

This data flow is shown in the following diagram:

../../_images/react-diagram.png

Stores listen to actions. Components subscribe to stores.

Note

For more information on stores, refer to the Redux documentation.

The user plugin’s store is called UsersStore. It keeps the following state variables:

users An array of objects representing the users of the currently selected database. For details on the fields returned in these objects, see User Object.
database A string representing the name of the currently selected database. This string is used in the store to filter the users displayed. Compass will only display users whose authentication database is the database currently being viewed.
error An optional object containing error information.

User Object

The objects in the users state variable take the following form:

_id A unique identifier for the user, incorporating the user’s name and authentication database.
user The user’s username.
db The database on which the user was created.
roles

An array of objects representing the roles of the user and the databases in which those roles apply.

For example:

"roles" : [
  {
    "role" : "readWrite",
    "db" : "test"
  }
]

Note

A user can have roles pertaining to different databases than the one on which they were created. For more information on roles, see Role-Based Access Control.

Implementing the Store

Update src/stores/store.js to match the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import Reflux from 'reflux';
import StateMixin from 'reflux-state-mixin';
import UsersActions from 'actions';

const debug = require('debug')('mongodb-compass:stores:users');

/**
 * Users store.
 */
const UsersStore = Reflux.createStore({
  /**
   * adds a state to the store, similar to React.Component's state
   * @see https://github.com/yonatanmn/Super-Simple-Flux#reflux-state-mixin
   *
   * If you call `this.setState({...})` this will cause the store to trigger
   * and push down its state as props to connected components.
   */
  mixins: [StateMixin.store],

  /**
   * listen to all actions defined in ../actions/index.jsx
   */
  listenables: UsersActions,

  /**
   * Initialize everything that is not part of the store's state.
   */
  init() {
  },

  /**
   * This method is called when all plugins are activated.
   */
  onActivated(appRegistry) {
    // Events emitted from the app registry:
    appRegistry.on('database-changed', this.onDatabaseChanged.bind(this));
  },

  /**
   * Initialize the Users store state. The returned object must
   * contain all keys that you might want to modify with this.setState().
   *
   * @return {Object} initial store state.
   */
  getInitialState() {
    return {
      status: 'enabled',
      users: [],
      database: '',
      error: null
    };
  },

  _setDatabaseUsers(dbName) {
    const filter = {db: dbName};

    const findOptions = {
      fields: { user: 1, db: 1, credentials: 1, roles: 1 }
    };

    window.app.dataService.find('admin.system.users', filter, findOptions,
                               (findError, documents) => {
      if (findError) {
        this.setState({error: findError});
        return;
      }
      this.setState({database: dbName});
      this.setState({users: documents});
    });
  },

  onDatabaseChanged(namespace) {
    if (!namespace || namespace.includes('.') || namespace === this.state.database) {
      return;
    }
    this._setDatabaseUsers(namespace);
  },

  /**
   * log changes to the store as debug messages.
   * @param  {Object} prevState   previous state.
   */
  storeDidUpdate(prevState) {
    debug('Users store changed from', prevState, 'to', this.state);
  }
});

export default UsersStore;
export { UsersStore };

Creating the Component

The users view contains a component, which is a view which renders based on the UsersStore store.

Components are written in JSX, which allows HTML to be inserted and rendered elegantly with ReactJS. HTML is rendered by the render method.

Note

For more information on components, refer to the ReactJS documentation.

Update src/components/users/users.jsx to match the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import styles from './Users.less';

class Users extends Component {
  static displayName = 'UsersComponent';

  static propTypes = {
    users: PropTypes.array.isRequired,
    database: PropTypes.string,
    error: PropTypes.object
  };

  static defaultProps = {
    users: [],
    database: '',
    error: null
  };

  onClick = () => {
    this.props.actions.toggleStatus();
  }

  /**
   * Render Users component.
   *
   * @returns {React.Component} The rendered component.
   */
  render() {
    return (
      <div className={classnames(styles.root)}>
        <h2 className={classnames(styles.title)}>{this.props.database} Users</h2>
        <table>
          <tr>
            <th>User</th>
            <th>Database</th>
            <th>Role</th>
          </tr>
          {
            this.props.users.map((user) => {
              return user.roles.map((role) => {
                return (
                  <tr>
                    <td>{user.user}</td>
                    <td>{role.db}</td>
                    <td>{role.role}</td>
                  </tr>
                 )
               });
            })
          }
        </table>
      </div>
    );
  }
}

export default Users;
export { Users };

This component displays a table showing each user whose authentication database is the database currently being viewed, along with all of the roles assigned to that user.

Styling the Component

The users.jsx component imports its styles from the users.less file also located in the src/components/users/ directory.

Update src/components/users/users.less to match the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@import "~less/compass/_theme.less";

.root {
	background: #0e83cd;
	color: @pw;
	height: 100vh;
	width: 100vw;
	padding: 2.4rem;
}

.title {
	margin-top: 0;
}

table {
	width: 50vw;
}

MongoDB Compass plugins implement CSS Modules and Less syntax to style components.

For more information on styling your MongoDB Compass plugins, see Styling.

Running the Plugin

Run the following command from your plugin’s root directory to build the plugin:

npm run compile

Launch Compass to view your plugin.

Extending the Plugin

One option to extend this plugin is allowing the MongoDB Compass user to assign new user roles as well as edit and delete existing roles, provided they have sufficient priviliges to execute such actions.