# Conflicts: # src/_store/system.module.js # src/account/NetworkPage.vuepull/8/head
@@ -9,6 +9,7 @@ export const deviceService = { | |||
getDevicesByUserId, | |||
getAllDevicesByUserId, | |||
getAllDeviceTypesByUserId, | |||
setDeviceSecurityLevel, | |||
getDeviceQRcodeById, | |||
getDeviceVPNconfById, | |||
addDeviceByUserId, | |||
@@ -27,6 +28,10 @@ function getAllDeviceTypesByUserId(userId, messages, errors) { | |||
return fetch(`${config.apiUrl}/users/${userId}/deviceTypes`, util.getWithAuth()).then(util.handleCrudResponse(messages, errors)); | |||
} | |||
function setDeviceSecurityLevel(userId, deviceId, securityLevel, messages, errors) { | |||
return fetch(`${config.apiUrl}/users/${userId}/devices/${deviceId}/securityLevel/${securityLevel}`, util.postWithAuth()).then(util.handleCrudResponse(messages, errors)); | |||
} | |||
function getDeviceQRcodeById(userId, deviceId, messages, errors) { | |||
return fetch(`${config.apiUrl}/users/${userId}/devices/${deviceId}/vpn/QR.png.base64`, util.getWithAuth()).then(util.handlePlaintextResponse(messages, errors)); | |||
} | |||
@@ -270,7 +270,10 @@ const mutations = { | |||
state.user = null; | |||
}, | |||
appLoginRequest(state) {}, | |||
appLoginRequest(state) { | |||
state.loginError = null; | |||
state.status = Object.assign({}, state.status, {loggingIn: true}); | |||
}, | |||
appLoginSuccess(state, {user, uri}) { | |||
if (user.token) { | |||
if (util.currentUser() === null) { | |||
@@ -46,6 +46,15 @@ const actions = { | |||
); | |||
}, | |||
setDeviceSecurityLevel({ commit }, {userId, deviceId, securityLevel, messages, errors}) { | |||
commit('setDeviceSecurityLevelRequest'); | |||
deviceService.setDeviceSecurityLevel(userId, deviceId, securityLevel, messages, errors) | |||
.then( | |||
device => commit('setDeviceSecurityLevelSuccess', device), | |||
error => commit('setDeviceSecurityLevelFailure', error) | |||
); | |||
}, | |||
getDeviceQRcodeById({ commit }, {userId, deviceId, messages, errors}) { | |||
commit('getDeviceQRcodeByIdRequest'); | |||
deviceService.getDeviceQRcodeById(userId, deviceId, messages, errors) | |||
@@ -108,6 +117,17 @@ const mutations = { | |||
state.error = error; | |||
}, | |||
setDeviceSecurityLevelRequest(state) { | |||
state.loading.setSecurityLevel = true; | |||
}, | |||
setDeviceSecurityLevelSuccess(state, device) { | |||
state.loading.setSecurityLevel = false; | |||
}, | |||
setDeviceSecurityLevelFailure(state, error) { | |||
state.loading.setSecurityLevel = false; | |||
state.error = error; | |||
}, | |||
getDeviceQRcodeByIdRequest(state) { | |||
state.loading.qrCode = true; | |||
state.qrCodeImageBase64 = null; | |||
@@ -22,7 +22,8 @@ const state = { | |||
promoCodePolicy: null, | |||
requireSendMetrics: null, | |||
isInRestoringStatus: false, | |||
support: {} | |||
support: {}, | |||
securityLevels: null | |||
}, | |||
entityConfigs: {}, | |||
searchResults: [], | |||
@@ -8,11 +8,13 @@ | |||
<thead> | |||
<tr> | |||
<th nowrap="nowrap">{{messages.label_field_device_name}}</th> | |||
<th nowrap="nowrap">{{messages.label_field_device_type}}</th> | |||
<!-- <th nowrap="nowrap">{{messages.label_field_device_type}}</th>--> | |||
<th nowrap="nowrap">{{messages.label_field_device_app}}</th> | |||
<!-- <th nowrap="nowrap">{{messages.label_field_device_enabled}}</th>--> | |||
<th>{{messages.label_field_device_vpn_config}}</th> | |||
<th nowrap="nowrap">{{messages.label_field_device_certificate}}</th> | |||
<th>{{messages.label_field_device_ctime}}</th> | |||
<th nowrap="nowrap">{{messages.label_field_security_level}}</th> | |||
<!-- <th>{{messages.label_field_device_ctime}}</th>--> | |||
<th>{{messages.label_field_device_help}}</th> | |||
<th><!-- delete --></th> | |||
</tr> | |||
@@ -20,32 +22,57 @@ | |||
<tbody> | |||
<tr v-for="device in devices"> | |||
<td nowrap="nowrap">{{device.name}}</td> | |||
<td nowrap="nowrap">{{messages['device_type_'+device.deviceType]}}</td> | |||
<!-- <td nowrap="nowrap">--> | |||
<!-- <i :class="messages['device_type_icon_'+device.deviceType]+' bubble-app-icon'"></i><br/>--> | |||
<!-- {{messages['device_type_'+device.deviceType]}}--> | |||
<!-- </td>--> | |||
<td align="center"> | |||
<a v-if="appLinks" target="_blank" rel="noopener noreferrer" :href="appLinks[device.deviceType]"> | |||
<i :class="messages['device_type_icon_'+device.deviceType]+' bubble-app-icon'"></i><br/> | |||
{{messages.message_download_app}} | |||
</a> | |||
</td> | |||
<!-- <td>{{messages['message_'+device.enabled]}}</td>--> | |||
<td> | |||
<div v-if="displayVpnConfig[device.uuid] === true" class="device-vpn-config-div"> | |||
<h3>{{device.name}}</h3> | |||
<hr/> | |||
<div v-if="qrCodeImageBase64"> | |||
<div v-if="qrCodeImageBase64 && messages['device_type_vpn_'+device.deviceType] === 'show_qr_code'"> | |||
<img :src="'data:image/png;base64,'+qrCodeImageBase64"/> | |||
<div v-if="errors.has('deviceQRcode')" class="invalid-feedback d-block">{{ errors.first('deviceQRcode') }}</div> | |||
</div> | |||
<div v-else-if="vpnConfBase64 && messages['device_type_vpn_'+device.deviceType] === 'download_conf'"> | |||
<button v-if="vpnConfBase64" @click="util.downloadURI('data:text/plain;base64,'+vpnConfBase64, 'vpn.conf')">{{messages.message_device_vpn_download_conf}}</button> | |||
<div v-if="errors.has('deviceVpnConf')" class="invalid-feedback d-block">{{ errors.first('deviceVpnConf') }}</div> | |||
</div> | |||
<div v-else> | |||
<h5>{{messages.message_device_vpn_unavailable}}</h5> | |||
</div> | |||
<div v-if="errors.has('deviceQRcode')" class="invalid-feedback d-block">{{ errors.first('deviceQRcode') }}</div> | |||
<hr/> | |||
<button v-if="vpnConfBase64" @click="util.downloadURI('data:text/plain;base64,'+vpnConfBase64, 'vpn.conf')">{{messages.message_device_vpn_download_conf}}</button> | |||
<div v-if="errors.has('deviceVpnConf')" class="invalid-feedback d-block">{{ errors.first('deviceVpnConf') }}</div> | |||
<hr/> | |||
<button @click="hideVpnConfig()" class="btn btn-primary">{{messages.button_label_close_device_vpn_config}}</button> | |||
</div> | |||
<div v-else-if="messages['!button_label_vpn_config_'+messages['device_type_vpn_'+device.deviceType]]"> | |||
<button @click="showVpnConfig(device.uuid)">{{messages['button_label_vpn_config_'+messages['device_type_vpn_'+device.deviceType]]}}</button> | |||
</div> | |||
<div v-else> | |||
<button @click="showVpnConfig(device.uuid)">{{messages.message_device_vpn_show_config}}</button> | |||
{{messages.message_device_vpn_unavailable}} | |||
</div> | |||
</td> | |||
<td nowrap="nowrap"><a v-if="device.deviceType" :href="'/api/auth/cacert?deviceType='+device.deviceType">{{messages['device_type_'+device.deviceType]}}</a></td> | |||
<td nowrap="nowrap">{{messages.label_device_ctime_format.parseDateMessage(device.ctime, messages)}}</td> | |||
<td nowrap="nowrap"> | |||
<a :href="'/api/auth/cacert?deviceType='+device.deviceType"> | |||
{{messages.message_download_ca_cert}} | |||
</a> | |||
</td> | |||
<td> | |||
<form v-if="configs.securityLevels" @submit.prevent="false"> | |||
<input type="hidden" name="deviceId" :value="device.uuid"/> | |||
<select v-model="device.securityLevel" @change="setSecurityLevel($event)"> | |||
<option v-for="level in configs.securityLevels" :value="level">{{messages['device_security_level_'+level]}}</option> | |||
</select> | |||
</form> | |||
</td> | |||
<!-- <td nowrap="nowrap">{{messages.label_device_ctime_format.parseDateMessage(device.ctime, messages)}}</td>--> | |||
<td> | |||
<div v-if="displayDeviceHelp[device.uuid] === true" class="device-help-div"> | |||
<div v-html="messages['device_type_'+device.deviceType+'_help_html']"></div> | |||
@@ -76,10 +103,10 @@ | |||
<div v-if="submitted && errors.has('deviceName')" class="invalid-feedback d-block">{{ errors.first('deviceName') }}</div> | |||
</div> | |||
<div class="form-group" v-if="deviceTypes"> | |||
<div class="form-group" v-if="addableDeviceTypes"> | |||
<label htmlFor="deviceType">{{messages.label_field_device_type}}</label> | |||
<select v-model="deviceType" name="deviceType" class="form-control"> | |||
<option v-for="type in deviceTypes" :value="type">{{messages['device_type_'+type]}}</option> | |||
<option v-for="type in addableDeviceTypes" :value="type">{{messages['device_type_'+type]}}</option> | |||
</select> | |||
</div> | |||
@@ -92,15 +119,18 @@ | |||
<hr/> | |||
<div> | |||
<h4>{{messages.message_download_ca_cert}}</h4> | |||
<!-- todo: use a v-for here, don't explicitly list deviceTypes, which may change --> | |||
<a href="/api/auth/cacert?deviceType=macosx">{{messages.message_os_macosx}}</a> | | |||
<a href="/api/auth/cacert?deviceType=ios">{{messages.message_os_ios}}</a> | | |||
<a href="/api/auth/cacert?deviceType=windows">{{messages.message_os_windows}}</a> | | |||
<a href="/api/auth/cacert?deviceType=android">{{messages.message_os_android}}</a> | | |||
<a href="/api/auth/cacert?deviceType=linux">{{messages.message_os_linux}}</a> | | |||
<a href="/api/auth/cacert?deviceType=firefox">{{messages.message_os_firefox}}</a> | |||
<div v-if="certDeviceTypes"> | |||
<table border="0" width="100%"> | |||
<tr><td colspan="5"><h4>{{messages.message_download_ca_cert}}</h4></td></tr> | |||
<tr> | |||
<td v-for="certDevice in certDeviceTypes" align="center" :width="certDeviceWidth+'%'"> | |||
<a :href="'/api/auth/cacert?deviceType='+certDevice"> | |||
<i :class="messages['device_type_icon_'+certDevice]+' bubble-app-icon'"></i><br/> | |||
{{messages['device_type_'+certDevice]}} | |||
</a> | |||
</td> | |||
</tr> | |||
</table> | |||
<hr/> | |||
</div> | |||
</div> | |||
@@ -128,10 +158,28 @@ | |||
}, | |||
computed: { | |||
...mapState('devices', ['deviceTypes', 'devices', 'device', 'qrCodeImageBase64', 'vpnConfBase64']), | |||
...mapState('system', ['messages']), | |||
...mapState('system', ['messages', 'appLinks', 'configs']), | |||
...mapGetters('devices', ['loading']), | |||
addDeviceReady: function () { | |||
return this.deviceName !== null && this.deviceName !== '' && this.deviceType !== null && this.deviceType !== ''; | |||
}, | |||
addableDeviceTypes: function () { | |||
if (this.messages && this.messages['!addable_device_types']) { | |||
return this.messages['addable_device_types'].split('|'); | |||
} | |||
return []; | |||
}, | |||
addableDeviceWidth: function () { | |||
return 100.0/this.addableDeviceTypes.length | |||
}, | |||
certDeviceTypes: function () { | |||
if (this.messages && this.messages['!cert_device_types']) { | |||
return this.messages['cert_device_types'].split('|'); | |||
} | |||
return []; | |||
}, | |||
certDeviceWidth: function () { | |||
return 100.0/this.certDeviceTypes.length | |||
} | |||
}, | |||
created () { | |||
@@ -144,7 +192,10 @@ | |||
userId: this.userId, | |||
messages: this.messages, | |||
errors: this.errors | |||
}) | |||
}); | |||
const user = util.currentUser(); | |||
if (user) this.getAppLinks(user.locale); | |||
this.loadSystemConfigs(); | |||
}, | |||
mounted() { | |||
window.addEventListener('keyup', ev => { | |||
@@ -157,8 +208,9 @@ | |||
methods: { | |||
...mapActions('devices', [ | |||
'getAllDeviceTypesByUserId', 'getDevicesByUserId', 'addDeviceByUserId', 'removeDeviceByUserId', | |||
'getDeviceQRcodeById', 'getDeviceVPNconfById' | |||
'getDeviceQRcodeById', 'getDeviceVPNconfById', 'setDeviceSecurityLevel' | |||
]), | |||
...mapActions('system', ['getAppLinks', 'loadSystemConfigs']), | |||
addDevice () { | |||
this.errors.clear(); | |||
this.submitted = true; | |||
@@ -182,7 +234,17 @@ | |||
errors: this.errors | |||
}); | |||
}, | |||
setSecurityLevel (event) { | |||
this.setDeviceSecurityLevel({ | |||
userId: this.userId, | |||
deviceId: event.target.form.deviceId.value, | |||
securityLevel: event.target.value, | |||
messages: this.messages, | |||
errors: this.errors | |||
}); | |||
}, | |||
showVpnConfig (id) { | |||
this.hideDeviceHelp(); | |||
const show = {}; | |||
show[id] = true; | |||
this.errors.clear(); | |||
@@ -203,6 +265,7 @@ | |||
hideVpnConfig () { this.displayVpnConfig = {}; }, | |||
showDeviceHelp (id) { | |||
this.hideVpnConfig(); | |||
this.displayDeviceHelp = {}; | |||
this.displayDeviceHelp[id] = true; | |||
}, | |||
@@ -214,7 +277,7 @@ | |||
// after device added, clear device fields | |||
this.deviceName = null; | |||
this.deviceType = null; | |||
if (dev.uuid) this.displayDeviceHelp[dev.uuid] = true; | |||
if (dev.uuid) this.showVpnConfig(dev.uuid); | |||
} | |||
} | |||
} | |||
@@ -20,7 +20,7 @@ | |||
<h5 v-html="messages.title_launch_help_html"></h5> | |||
<div v-if="network && network.state === 'running'" v-html="messages.message_launch_success_help_html"></div> | |||
<div v-else v-html="messages.message_launch_help_html"></div> | |||
<div v-if="appLinks"> | |||
<div v-if="appLinks && addableDeviceTypes"> | |||
<div v-if="network && network.state === 'running'" v-html="messages.message_launch_success_apps"></div> | |||
<div v-else v-html="messages.message_launch_help_apps"></div> | |||
<br/> | |||
@@ -204,6 +204,15 @@ | |||
showSetupHelp () { | |||
return (this.network !== null && this.network.uuid !== this.configs.networkUuid | |||
&& (this.network.state === 'running' || this.network.state === 'starting')); | |||
}, | |||
addableDeviceTypes: function () { | |||
if (this.messages && this.messages['!addable_device_types']) { | |||
return this.messages['addable_device_types'].split('|'); | |||
} | |||
return []; | |||
}, | |||
addableDeviceWidth: function () { | |||
return 100.0/this.addableDeviceTypes.length | |||
} | |||
}, | |||
methods: { | |||
@@ -41,17 +41,17 @@ | |||
<div v-if="submitted && errors.has('password')" class="invalid-feedback d-block">{{ errors.first('password') }}</div> | |||
</div> | |||
<div class="form-group"> | |||
<label htmlFor="url">{{messages.field_label_url}}</label> | |||
<input type="text" v-model="subject.url" name="url" class="form-control"/> | |||
<div v-if="submitted && errors.has('url')" class="invalid-feedback d-block">{{ errors.first('url') }}</div> | |||
</div> | |||
<!-- <div class="form-group">--> | |||
<!-- <label htmlFor="url">{{messages.field_label_url}}</label>--> | |||
<!-- <input type="text" v-model="subject.url" name="url" class="form-control"/>--> | |||
<!-- <div v-if="submitted && errors.has('url')" class="invalid-feedback d-block">{{ errors.first('url') }}</div>--> | |||
<!-- </div>--> | |||
<div class="form-group"> | |||
<label htmlFor="description">{{messages.field_label_description}}</label> | |||
<input type="text" v-model="subject.description" name="description" class="form-control"/> | |||
<div v-if="submitted && errors.has('description')" class="invalid-feedback d-block">{{ errors.first('description') }}</div> | |||
</div> | |||
<!-- <div class="form-group">--> | |||
<!-- <label htmlFor="description">{{messages.field_label_description}}</label>--> | |||
<!-- <input type="text" v-model="subject.description" name="description" class="form-control"/>--> | |||
<!-- <div v-if="submitted && errors.has('description')" class="invalid-feedback d-block">{{ errors.first('description') }}</div>--> | |||
<!-- </div>--> | |||
<div v-if="admin === true && currentUser.uuid !== subject.uuid" class="form-group"> | |||
<label for="admin">{{messages.field_label_administrator}}</label> | |||
@@ -219,9 +219,9 @@ | |||
watch: { | |||
user (u) { | |||
if (u) { | |||
console.log('watch.user: received: '+JSON.stringify(u)); | |||
// console.log('watch.user: received: '+JSON.stringify(u)); | |||
if (this.submitted) { | |||
console.log('watch.user: action was successful, refreshing page'); | |||
// console.log('watch.user: action was successful, refreshing page'); | |||
if (this.me) { | |||
this.$router.push('/'); | |||
} else if (this.admin) { | |||
@@ -105,7 +105,7 @@ | |||
watch: { | |||
users (uList) { | |||
if (uList !== null && this.lastAction !== null) { | |||
console.log('watch.users: updated, refreshing...'); | |||
// console.log('watch.users: updated, refreshing...'); | |||
this.lastAction = null; | |||
this.refreshData() | |||
} | |||
@@ -92,7 +92,11 @@ export default { | |||
} else { | |||
const user = util.currentUser(); | |||
if (user !== null && (typeof user.token !== 'undefined' && user.token !== null)) { | |||
this.checkSession({messages: this.messages, errors: this.errors}); | |||
if (this.status.loggingIn || this.status.loggedIn) { | |||
// console.log('activated: this.status.loggingIn='+this.status.loggingIn+', this.status.loggedIn='+this.status.loggedIn+', not checking session'); | |||
} else { | |||
this.checkSession({messages: this.messages, errors: this.errors}); | |||
} | |||
} | |||
} | |||
}, | |||
@@ -1,40 +0,0 @@ | |||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||
<template> | |||
<div> | |||
<h2>{{messages.title_check_certificate}}</h2> | |||
<hr/> | |||
<div v-if="cert_verified !== null"> | |||
<h4>{{messages['check_cert_'+cert_verified]}}</h4> ({{cert_verified}}) | |||
</div> | |||
<!-- <iframe style="visibility: hidden" id="cert_check_frame" v-if="configs && configs.certValidationHost" :src="'https://'+configs.certValidationHost" @load="frameLoaded()"></iframe>--> | |||
<iframe id="cert_check_frame" v-if="configs && configs.certValidationHost" :src="'https://'+configs.certValidationHost" @load="frameLoaded()"></iframe> | |||
</div> | |||
</template> | |||
<script> | |||
import { mapState, mapActions } from 'vuex' | |||
export default { | |||
data() { | |||
return { | |||
cert_verified: null | |||
} | |||
}, | |||
computed: { | |||
...mapState('system', ['messages', 'configs']) | |||
}, | |||
created() { | |||
this.loadSystemConfigs(); | |||
const vue = this; | |||
window.setTimeout(() => { | |||
if (vue.cert_verified === null) vue.cert_verified = false; | |||
}, 10000); | |||
}, | |||
methods: { | |||
...mapActions('system', ['loadSystemConfigs']), | |||
frameLoaded() { | |||
console.log('frame loaded!'); | |||
this.cert_verified = true; | |||
} | |||
} | |||
}; | |||
</script> |
@@ -53,6 +53,14 @@ | |||
methods: { | |||
...mapActions('account', ['login', 'logout', 'appLogin']), | |||
...mapActions('system', ['loadSystemConfigs', 'loadMessages']), | |||
}, | |||
watch: { | |||
user (u) { | |||
if (u.token) { | |||
this.loadMessages('post_auth', u.locale); | |||
this.loadMessages('apps', u.locale); | |||
} | |||
} | |||
} | |||
}; | |||
</script> |
@@ -132,17 +132,6 @@ export default { | |||
if (this.user.password !== this.confirmPassword) { | |||
this.errors.add({field: 'confirmPassword', msg: this.messages['err_confirmPassword_mismatch']}) | |||
} else { | |||
if (this.plan !== null) { | |||
// if (this.paymentMethodObject === null) { | |||
// this.errors.add({field: 'paymentMethod', msg: this.messages['err_paymentMethod_required']}); | |||
// return; | |||
// } else { | |||
console.log('handleSubmit: setting this.user.preferredPlan = '+this.plan.uuid); | |||
this.user.preferredPlan = this.plan.uuid; | |||
// this.user.paymentMethodObject = this.paymentMethodObject; | |||
// this.user.paymentMethodObject.cloud = this.selectedPaymentMethod.name; | |||
// } | |||
} | |||
this.register({ | |||
user: this.user, | |||
messages: this.messages, | |||
@@ -174,6 +163,11 @@ export default { | |||
if (apm) { | |||
this.paymentMethodObject = apm; | |||
} | |||
}, | |||
plan (p) { | |||
if (p.uuid) { | |||
this.user.preferredPlan = p.uuid; | |||
} | |||
} | |||
} | |||
}; | |||