Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add users page to parliament #2530

Merged
merged 4 commits into from Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG
Expand Up @@ -98,6 +98,7 @@ NOTICE: Create a parliament config file before upgrading (see https://arkime.com
## Parliament
- #2377 dashboard-only mode removed, if you want users to just see the dashboard don't assign them the parliamentUser role
- #2395 configuration is now stored in opensearch/elasticsearch
- #2530 add Users page


4.6.0 2023/10/16
Expand Down
4 changes: 4 additions & 0 deletions cont3xt/vueapp/src/store.js
@@ -1,3 +1,7 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';
Expand Down
13 changes: 12 additions & 1 deletion parliament/parliament.js
Expand Up @@ -33,6 +33,7 @@ const version = require('../common/version');
const Notifier = require('../common/notifier');
const ArkimeUtil = require('../common/arkimeUtil');
const ArkimeConfig = require('../common/arkimeConfig');
const jsonParser = ArkimeUtil.jsonParser;

// ----------------------------------------------------------------------------
// APP SETUP
Expand Down Expand Up @@ -135,7 +136,7 @@ const cspDirectives = {
// need unsafe-eval for vue full build: https://vuejs.org/v2/guide/installation.html#CSP-environments
scriptSrc: ["'self'", "'unsafe-eval'", (req, res) => `'nonce-${res.locals.nonce}'`],
objectSrc: ["'none'"],
imgSrc: ["'self'"]
imgSrc: ["'self'", 'data:']
};
if (process.env.NODE_ENV === 'development') {
// need unsafe inline styles for hot module replacement
Expand Down Expand Up @@ -1509,6 +1510,16 @@ app.put('/parliament/api/settings', [isAdmin, checkCookieToken], Parliament.apiU
// Update the parliament general settings object to the defaults
app.put('/parliament/api/settings/restoreDefaults', [isAdmin, checkCookieToken], Parliament.apiRestoreDefaultSettings);

// user endpoints
app.get('/parliament/api/user', User.apiGetUser);
app.post('/parliament/api/users', [jsonParser, User.checkRole('usersAdmin'), setCookie], User.apiGetUsers);
Dismissed Show dismissed Hide dismissed
app.post('/parliament/api/users/csv', [jsonParser, User.checkRole('usersAdmin'), setCookie], User.apiGetUsersCSV);
Dismissed Show dismissed Hide dismissed
app.post('/parliament/api/user', [jsonParser, checkCookieToken, User.checkRole('usersAdmin')], User.apiCreateUser);
Dismissed Show dismissed Hide dismissed
app.post('/parliament/api/user/password', [jsonParser, checkCookieToken, Auth.getSettingUserDb], User.apiUpdateUserPassword);
Dismissed Show dismissed Hide dismissed
app.delete('/parliament/api/user/:id', [jsonParser, checkCookieToken, User.checkRole('usersAdmin')], User.apiDeleteUser);
Dismissed Show dismissed Hide dismissed
app.post('/parliament/api/user/:id', [jsonParser, checkCookieToken, User.checkRole('usersAdmin')], User.apiUpdateUser);
Dismissed Show dismissed Hide dismissed
app.post('/parliament/api/user/:id/assignment', [jsonParser, checkCookieToken, User.checkAssignableRole], User.apiUpdateUserRole);
Dismissed Show dismissed Hide dismissed

// user roles endpoint
app.get('/parliament/api/user/roles', [ArkimeUtil.noCacheJson, checkCookieToken], User.apiRoles);

Expand Down
4 changes: 4 additions & 0 deletions parliament/vueapp/src/auth.js
@@ -1,3 +1,7 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import Vue from 'vue';
import store from '@/store';

Expand Down
8 changes: 8 additions & 0 deletions parliament/vueapp/src/components/Navbar.vue
Expand Up @@ -44,6 +44,14 @@ SPDX-License-Identifier: Apache-2.0
Settings
</router-link>
</li>
<li class="nav-item mr-2"
v-if="isAdmin">
<router-link to="users"
active-class="active"
class="nav-link">
Users
</router-link>
</li>
</ul> <!-- /page links -->
<!-- version -->
<span class="pr-2">
Expand Down
18 changes: 4 additions & 14 deletions parliament/vueapp/src/components/Settings.vue
Expand Up @@ -313,7 +313,7 @@ SPDX-License-Identifier: Apache-2.0
</span>
</div>
<p class="form-text small text-muted">
Configure the Parliament's hostname to add a link to the Parliament Dashbaord to every alert
Configure the Parliament's hostname to add a link to the Parliament Dashboard to every alert
</p>
</div>
</div> <!-- /hostname -->
Expand All @@ -337,8 +337,8 @@ SPDX-License-Identifier: Apache-2.0

<script>
import SettingsService from './settings.service';
import UserService from '@/components/user.service';
import Notifiers from '../../../../common/vueapp/Notifiers';
import setReqHeaders from '../../../../common/vueapp/setReqHeaders';

let inputDebounce;
let msgCloseTimeout;
Expand Down Expand Up @@ -371,6 +371,8 @@ export default {
}
},
mounted: function () {
UserService.getRoles();

// does the url specify a tab in hash
let tab = window.location.hash;
if (tab) { // if there is a tab specified and it's a valid tab
Expand All @@ -379,8 +381,6 @@ export default {
this.visibleTab = tab;
}
}

this.loadRoles();
},
methods: {
/* page functions ------------------------------------------------------ */
Expand Down Expand Up @@ -464,16 +464,6 @@ export default {
this.msgType = type;
},
/* helper functions ---------------------------------------------------- */
loadRoles: function () {
fetch('api/user/roles', {
method: 'GET',
headers: setReqHeaders({ 'Content-Type': 'application/json' })
}).then((response) => {
return response.json();
}).then((response) => {
this.$store.commit('setRoles', response.roles || []);
});
},
clearMessage: function (time) {
if (msgCloseTimeout) { clearTimeout(msgCloseTimeout); }
msgCloseTimeout = setTimeout(() => {
Expand Down
52 changes: 52 additions & 0 deletions parliament/vueapp/src/components/Users.vue
@@ -0,0 +1,52 @@
<!--
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
-->
<template>
<div class="container-fluid overflow-auto">
<UsersCommon
v-if="getUser"
:roles="getRoles"
parent-app="Parliament"
:current-user="getUser"
@update-roles="updateRoles"
@update-current-user="updateCurrentUser">
</UsersCommon>
<div v-else>
<!-- error that we can't fetch the user -->
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">Error</h4>
<p>There was an error fetching the user.</p>
</div>
</div>
</div>
</template>

<script>
import { mapGetters } from 'vuex';

import UsersCommon from '../../../../common/vueapp/Users';
import UserService from '@/components/user.service';

export default {
name: 'Users',
components: { UsersCommon },
computed: {
...mapGetters(['getUser', 'getRoles'])
},
created () {
UserService.getUser();
UserService.getRoles();
},
methods: {
updateRoles () {
// NOTE: don't need to do anything with the data (the store does it)
UserService.getRoles();
},
updateCurrentUser () {
// NOTE: don't need to do anything with the data (the store does it)
UserService.getUser();
}
}
};
</script>
53 changes: 53 additions & 0 deletions parliament/vueapp/src/components/user.service.js
@@ -0,0 +1,53 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import Vue from 'vue';

import store from '@/store';
import setReqHeaders from '../../../../common/vueapp/setReqHeaders';

export default {
/**
* Fetches the list of user roles.
* @returns {Promise} - The promise that either resolves the request or rejects in error
*/
getUser () {
return new Promise((resolve, reject) => {
fetch('api/user').then((response) => {
if (!response.ok) { // test for bad response code
throw new Error(response.statusText);
}
return response.json();
}).then((response) => {
store.commit('setUser', response);
return resolve(response);
}).catch((err) => { // this catches an issue within the ^ .then
return reject(err);
});
});
},

/**
* Fetches the list of user roles.
* @returns {Promise} - The promise that either resolves the request or rejects in error
*/
getRoles () {
return new Promise((resolve, reject) => {
fetch('api/user/roles', {
headers: setReqHeaders()
}).then((response) => {
if (!response.ok) { // test for bad response code
throw new Error(response.statusText);
}
return response.json();
}).then((response) => {
const roles = Vue.filter('parseRoles')(response.roles);
store.commit('setRoles', roles);
return resolve(roles);
}).catch((err) => { // this catches an issue within the ^ .then
return reject(err);
});
});
}
};
4 changes: 4 additions & 0 deletions parliament/vueapp/src/filters.js
@@ -1,3 +1,7 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import Vue from 'vue';

/**
Expand Down
4 changes: 4 additions & 0 deletions parliament/vueapp/src/interceptors.js
@@ -1,3 +1,7 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import axios from 'axios';

export default function setup () {
Expand Down
4 changes: 4 additions & 0 deletions parliament/vueapp/src/main.js
@@ -1,3 +1,7 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
Expand Down
10 changes: 10 additions & 0 deletions parliament/vueapp/src/router.js
@@ -1,10 +1,15 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import Vue from 'vue';
import Router from 'vue-router';
import Parliament from '@/components/Parliament';
import Issues from '@/components/Issues';
import Settings from '@/components/Settings';
import Parliament404 from '@/components/404';
import Help from '@/components/Help';
import Users from '@/components/Users';
import AuthService from '@/auth';

Vue.use(Router);
Expand Down Expand Up @@ -42,6 +47,11 @@ const router = new Router({
name: 'Help',
component: Help
},
{
path: '/users',
name: 'Users',
component: Users
},
{
path: '*',
name: 'Not Found',
Expand Down
14 changes: 13 additions & 1 deletion parliament/vueapp/src/store.js
@@ -1,10 +1,15 @@
/*
Copyright Yahoo Inc.
SPDX-License-Identifier: Apache-2.0
*/
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
state: {
user: undefined,
roles: [],
notifiers: [],
theme: 'light',
Expand All @@ -14,6 +19,9 @@ const store = new Vuex.Store({
refreshInterval: 15000
},
mutations: {
setUser (state, value) {
state.user = value;
},
setTheme (state, value) {
state.theme = value;
},
Expand All @@ -29,7 +37,7 @@ const store = new Vuex.Store({
state.refreshInterval = value;
},
setRoles (state, value) {
state.roles = Vue.filter('parseRoles')(value);
state.roles = value || [];
},
setNotifiers (state, value) {
state.notifiers = value;
Expand All @@ -40,6 +48,10 @@ const store = new Vuex.Store({
setSettings (state, value) {
state.parliament.settings = value;
}
},
getters: {
getUser: state => state.user,
getRoles: state => state.roles
}
});

Expand Down