ovpn-admin/frontend/src/main.js

545 lines
16 KiB
JavaScript

import Vue from 'vue';
import axios from 'axios';
import VueQr from 'vue-qr'
import VueCookies from 'vue-cookies'
import BootstrapVue from 'bootstrap-vue'
import Notifications from 'vue-notification'
import VueGoodTablePlugin from 'vue-good-table'
Vue.use(VueCookies)
Vue.use(BootstrapVue)
Vue.use(Notifications)
Vue.use(VueGoodTablePlugin)
Vue.use(VueQr)
let axios_cfg = function(url, data='', type='form') {
if (data == '') {
return {
method: 'get',
url: url
};
} else if (type == 'form') {
return {
method: 'post',
url: url,
data: data,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
};
} else if (type == 'file') {
return {
method: 'post',
url: url,
data: data,
headers: { 'Content-Type': 'multipart/form-data' }
};
} else if (type == 'json') {
return {
method: 'post',
url: url,
data: data,
headers: { 'Content-Type': 'application/json' }
};
}
};
new Vue({
el: '#app',
data: {
columns: [
{
label: 'Name',
field: 'Identity',
// filterable: true,
},
{
label: 'Account Status',
field: 'AccountStatus',
filterable: true,
},
{
label: 'Active Connections',
field: 'Connections',
filterable: true,
},
{
label: 'Expiration Date',
field: 'ExpirationDate',
type: 'date',
dateInputFormat: 'yyyy-MM-dd HH:mm:ss',
dateOutputFormat: 'yyyy-MM-dd HH:mm:ss',
formatFn: function (value) {
return value != "" ? value : ""
}
},
{
label: 'Revocation Date',
field: 'RevocationDate',
type: 'date',
dateInputFormat: 'yyyy-MM-dd HH:mm:ss',
dateOutputFormat: 'yyyy-MM-dd HH:mm:ss',
formatFn: function (value) {
return value != "" ? value : ""
}
},
{
label: 'Actions',
field: 'actions',
sortable: false,
tdClass: 'text-right',
globalSearchDisabled: true,
},
],
rows: [],
actions: [
{
name: 'u-change-password',
label: 'Change password',
class: 'btn-warning',
showWhenStatus: 'Active',
showForServerRole: ['master'],
showForModule: ['passwdAuth'],
},
{
name: 'u-2fa',
label: '2FA',
class: 'btn-success',
showWhenStatus: 'Active',
showForServerRole: ['master'],
showForModule: ['totpAuth'],
},
{
name: 'u-revoke',
label: 'Revoke',
class: 'btn-warning',
showWhenStatus: 'Active',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-delete',
label: 'Delete',
class: 'btn-danger',
showWhenStatus: 'Revoked',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-delete',
label: 'Delete',
class: 'btn-danger',
showWhenStatus: 'Expired',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-rotate',
label: 'Rotate',
class: 'btn-warning',
showWhenStatus: 'Revoked',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-rotate',
label: 'Rotate',
class: 'btn-warning',
showWhenStatus: 'Expired',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-unrevoke',
label: 'Unrevoke',
class: 'btn-primary',
showWhenStatus: 'Revoked',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-download-config',
label: 'Download config',
class: 'btn-info',
showWhenStatus: 'Active',
showForServerRole: ['master', 'slave'],
showForModule: ["core"],
},
{
name: 'u-edit-ccd',
label: 'Edit routes',
class: 'btn-primary',
showWhenStatus: 'Active',
showForServerRole: ['master'],
showForModule: ["ccd"],
},
{
name: 'u-edit-ccd',
label: 'Show routes',
class: 'btn-primary',
showWhenStatus: 'Active',
showForServerRole: ['slave'],
showForModule: ["ccd"],
}
],
filters: {
hideRevoked: true,
},
serverRole: "master",
lastSync: "unknown",
modulesEnabled: [],
u: {
newUserName: '',
newUserPassword: '',
newPassword: '',
modalActionStatus: '',
modalActionMessage: '',
modalNewUserVisible: false,
modalShowCcdVisible: false,
modalChangePasswordVisible: false,
modalRotateUserVisible: false,
modalDeleteUserVisible: false,
modalRegister2faVisible: false,
openvpnConfig: '',
secret: '',
token: '',
twofaurl: '',
ccd: {
Name: '',
ClientAddress: '',
CustomRoutes: []
},
newRoute: {},
}
},
watch: {
},
mounted: function () {
this.getUserData();
this.getServerSetting();
this.filters.hideRevoked = this.$cookies.isKey('hideRevoked') ? (this.$cookies.get('hideRevoked') == "true") : false
},
created() {
let _this = this;
_this.$root.$on('u-revoke', function (msg) {
let data = new URLSearchParams();
data.append('username', _this.username);
axios.request(axios_cfg('api/user/revoke', data, 'form'))
.then(function(response) {
_this.getUserData();
_this.$notify({title: 'User ' + _this.username + ' revoked!', type: 'warn'})
}).catch(function(error) {
console.error()
_this.$notify({title: 'Failed to revoke user ' + _this.username , type: 'error'})
});
})
_this.$root.$on('u-unrevoke', function () {
let data = new URLSearchParams();
data.append('username', _this.username);
axios.request(axios_cfg('api/user/unrevoke', data, 'form'))
.then(function(response) {
_this.getUserData();
_this.$notify({title: 'User ' + _this.username + ' unrevoked!', type: 'success'})
}).catch(function(error) {
console.error()
_this.$notify({title: 'Failed to unrevoke user ' + _this.username , type: 'error'})
});
})
_this.$root.$on('u-rotate', function () {
_this.u.modalRotateUserVisible = true;
let data = new URLSearchParams();
data.append('username', _this.username);
})
_this.$root.$on('u-delete', function () {
_this.u.modalDeleteUserVisible = true;
let data = new URLSearchParams();
data.append('username', _this.username);
})
_this.$root.$on('u-download-config', function () {
let data = new URLSearchParams();
data.append('username', _this.username);
axios.request(axios_cfg('api/user/config/show', data, 'form'))
.then(function(response) {
const blob = new Blob([response.data], { type: 'text/plain' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = _this.username + ".ovpn"
link.click()
URL.revokeObjectURL(link.href)
}).catch(function(error) {
console.error()
_this.$notify({title: 'Failed to download config for user ' + _this.username , type: 'error'})
});
})
_this.$root.$on('u-edit-ccd', function () {
_this.u.modalShowCcdVisible = true;
let data = new URLSearchParams();
data.append('username', _this.username);
axios.request(axios_cfg('api/user/ccd', data, 'form'))
.then(function(response) {
_this.u.ccd = response.data;
});
})
_this.$root.$on('u-disconnect-user', function () {
_this.u.modalShowCcdVisible = true;
let data = new URLSearchParams();
data.append('username', _this.username);
axios.request(axios_cfg('api/user/disconnect', data, 'form'))
.then(function(response) {
console.log(response.data);
});
})
_this.$root.$on('u-change-password', function () {
_this.u.modalChangePasswordVisible = true;
let data = new URLSearchParams();
data.append('username', _this.username);
})
_this.$root.$on('u-2fa', function () {
_this.u.modalRegister2faVisible = true;
let data = new URLSearchParams();
data.append('username', _this.username);
data.append('secondfactor', _this.secondfactor);
data.append('token', _this.token);
_this.getUserTFAData(data);
})
},
computed: {
customAddressDynamic: function () {
return this.u.ccd.ClientAddress == "dynamic"
},
alertCssClass: function () {
return this.u.modalActionStatus == 200 ? "alert-success" : "alert-danger"
},
revokeFilterText: function() {
return this.filters.hideRevoked ? "Show revoked" : "Hide revoked"
},
filteredRows: function() {
if (this.filters.hideRevoked) {
return this.rows.filter(function(account) {
return account.AccountStatus == "Active"
});
} else {
return this.rows
}
}
},
methods: {
rowStyleClassFn: function(row) {
if (row.ConnectionStatus == 'Connected') {
return 'connected-user'
}
if (row.AccountStatus == 'Revoked') {
return 'revoked-user'
}
if (row.AccountStatus == 'Expired') {
return 'expired-user'
}
return ''
},
rowActionFn: function(e) {
this.username = e.target.dataset.username;
this.secondfactor = e.target.dataset.secondfactor;
this.$root.$emit(e.target.dataset.name);
},
getUserData: function() {
let _this = this;
axios.request(axios_cfg('api/users/list'))
.then(function(response) {
_this.rows = Array.isArray(response.data) ? response.data : [];
});
},
getUserTFAData: function(data) {
let _this = this;
if (!_this.secondfactor) {
axios.request(axios_cfg('api/user/2fa/secret', data, 'form'))
.then(function (response) {
_this.u.secret = response.data;
_this.u.twofaurl = "otpauth://totp/ovpn-" + _this.username + "?secret=" + _this.u.secret + "&issuer=OVPN";
});
}
},
registerUser2faApp: function(username) {
let _this = this;
let data = new URLSearchParams();
data.append('username', username);
data.append('token', _this.u.token);
axios.request(axios_cfg('api/user/2fa/register', data, 'form'))
.then(function(response) {
_this.u.modalActionStatus = 200;
_this.u.modalRegister2faVisible = false;
_this.getUserData();
_this.secondfactor = true;
_this.u.token = "";
_this.u.secret = "";
_this.$notify({title: '2FA application registered for user ' + username, type: 'success'});
})
.catch(function(error) {
_this.u.modalActionStatus = error.response.status;
_this.u.modalActionMessage = error.response.data.message;
_this.$notify({title: 'Register 2FA application for user ' + username + ' failed!', type: 'error'});
})
},
resetUser2faApp: function(username) {
let _this = this;
let data = new URLSearchParams();
data.append('username', username);
data.append('secondfactor', _this.secondfactor);
axios.request(axios_cfg('api/user/2fa/reset', data, 'form'))
.then(function(response) {
_this.u.modalActionStatus = 200;
_this.secondfactor = false;
_this.getUserTFAData(data);
_this.getUserData();
_this.$notify({title: '2FA application reset for user ' + username, type: 'success'});
})
.catch(function(error) {
_this.u.modalActionStatus = error.response.status;
_this.u.modalActionMessage = error.response.data.message;
_this.$notify({title: 'Reset 2FA application for user ' + username + ' failed!', type: 'error'});
})
},
getServerSetting: function() {
let _this = this;
axios.request(axios_cfg('api/server/settings'))
.then(function(response) {
_this.serverRole = response.data.serverRole;
_this.modulesEnabled = response.data.modules;
if (_this.serverRole == "slave") {
axios.request(axios_cfg('api/sync/last/successful'))
.then(function(response) {
_this.lastSync = response.data;
});
}
});
},
createUser: function() {
let _this = this;
_this.u.modalActionMessage = "";
let data = new URLSearchParams();
data.append('username', _this.u.newUserName);
data.append('password', _this.u.newUserPassword);
_this.username = _this.u.newUserName;
axios.request(axios_cfg('api/user/create', data, 'form'))
.then(function(response) {
_this.$notify({title: 'New user ' + _this.username + ' created', type: 'success'});
_this.u.modalNewUserVisible = false;
_this.u.newUserName = '';
_this.u.newUserPassword = '';
_this.getUserData();
})
.catch(function(error) {
_this.u.modalActionMessage = error.response.data;
_this.$notify({title: 'New user ' + _this.username + ' creation failed.', type: 'error'});
});
},
ccdApply: function() {
let _this = this;
_this.u.modalActionStatus= "";
_this.u.modalActionMessage = "";
axios.request(axios_cfg('api/user/ccd/apply', JSON.stringify(_this.u.ccd), 'json'))
.then(function(response) {
_this.u.modalActionStatus = 200;
_this.u.modalActionMessage = response.data;
_this.$notify({title: 'New CCD for user ' + _this.username + ' applied', type: 'success'});
})
.catch(function(error) {
_this.u.modalActionStatus = error.response.status;
_this.u.modalActionMessage = error.response.data.message;
_this.$notify({title: 'Apply new CCD for user ' + _this.username + 'failed ', type: 'error'});
});
},
changeUserPassword: function(username) {
let _this = this;
_this.u.modalActionMessage = "";
let data = new URLSearchParams();
data.append('username', username);
data.append('password', _this.u.newPassword);
axios.request(axios_cfg('api/user/change-password', data, 'form'))
.then(function(response) {
_this.u.modalActionStatus = 200;
_this.u.newPassword = '';
_this.getUserData();
_this.u.modalChangePasswordVisible = false;
_this.$notify({title: 'Password for user ' + username + ' changed!', type: 'success'});
})
.catch(function(error) {
_this.u.modalActionStatus = error.response.status;
_this.u.modalActionMessage = error.response.data.message;
_this.$notify({title: 'Changing password for user ' + username + ' failed!', type: 'error'});
});
},
rotateUser: function(username) {
let _this = this;
_this.u.modalActionMessage = "";
let data = new URLSearchParams();
data.append('username', username);
data.append('password', _this.u.newPassword);
axios.request(axios_cfg('api/user/rotate', data, 'form'))
.then(function(response) {
_this.u.modalActionStatus = 200;
_this.u.newPassword = '';
_this.getUserData();
_this.u.modalRotateUserVisible = false;
_this.$notify({title: 'Certificates for user ' + username + ' rotated!', type: 'success'});
})
.catch(function(error) {
_this.u.modalActionStatus = error.response.status;
_this.u.modalActionMessage = error.response.data.message;
_this.$notify({title: 'Rotate certificates for user ' + username + ' failed!', type: 'error'});
})
},
deleteUser: function(username) {
let _this = this;
_this.u.deleteUserMessage = "";
let data = new URLSearchParams();
data.append('username', username);
axios.request(axios_cfg('api/user/delete', data, 'form'))
.then(function(response) {
_this.u.modalActionStatus = 200;
_this.u.newPassword = '';
_this.getUserData();
_this.u.modalDeleteUserVisible = false;
_this.$notify({title: 'User ' + username + ' deleted!', type: 'success'});
})
.catch(function(error) {
_this.u.modalActionStatus = error.response.status;
_this.u.modalActionMessage = error.response.data.message;
_this.$notify({title: 'Deleting user ' + username + ' failed!', type: 'error'});
})
},
}
})