@@ -2,6 +2,7 @@ import Vue from 'vue'; | |||
import Router from 'vue-router'; | |||
import HomePage from '../account/HomePage' | |||
import ActivationPage from '../auth/ActivationPage' | |||
import RegisterPage from '../auth/RegisterPage' | |||
import LoginPage from '../auth/LoginPage' | |||
import MultifactorAuthPage from '../auth/MultifactorAuthPage' | |||
@@ -64,10 +65,13 @@ export const router = new Router({ | |||
] | |||
}, | |||
{ path: '/action', component: ActionPage }, | |||
{ path: '/activate', component: ActivationPage }, | |||
{ path: '/register', component: RegisterPage }, | |||
{ path: '/auth', component: MultifactorAuthPage }, | |||
{ path: '/login', component: LoginPage }, | |||
{ path: '/logout', component: LoginPage }, | |||
{ path: '/admin/accounts', component: AccountsPage }, | |||
{ path: '/admin/accounts/:uuid', component: ProfilePage }, | |||
@@ -77,7 +81,7 @@ export const router = new Router({ | |||
}); | |||
router.beforeEach((to, from, next) => { | |||
const publicPages = ['/login', '/logout', '/register', '/auth']; | |||
const publicPages = ['/login', '/logout', '/register', '/auth', '/activate']; | |||
const authRequired = !publicPages.includes(to.path); | |||
const user = util.currentUser(); | |||
@@ -26,33 +26,51 @@ export const util = { | |||
getWithAuth: function() { | |||
return { | |||
method: 'GET', | |||
headers: authHeader() | |||
headers: util.authHeader() | |||
}; | |||
}, | |||
entityNoAuth: function(method, obj) { | |||
if (typeof obj === 'undefined' || obj === null || obj === 'undefined') { | |||
return { | |||
method: method, | |||
headers: { 'Content-Type': 'application/json' } | |||
}; | |||
} else { | |||
return { | |||
method: method, | |||
headers: { 'Content-Type': 'application/json' }, | |||
body: JSON.stringify(obj) | |||
}; | |||
} | |||
}, | |||
postNoAuth: function(obj) { return util.entityNoAuth('POST', obj); }, | |||
putNoAuth: function(obj) { return util.entityNoAuth('PUT', obj); }, | |||
entityWithAuth: function(method, obj) { | |||
if (typeof obj === 'undefined' || obj === null || obj === 'undefined') { | |||
return { | |||
method: method, | |||
headers: { ...authHeader(), 'Content-Type': 'application/json' } | |||
headers: { ...util.authHeader(), 'Content-Type': 'application/json' } | |||
}; | |||
} else { | |||
return { | |||
method: method, | |||
headers: { ...authHeader(), 'Content-Type': 'application/json' }, | |||
headers: { ...util.authHeader(), 'Content-Type': 'application/json' }, | |||
body: JSON.stringify(obj) | |||
}; | |||
} | |||
}, | |||
postWithAuth: function(obj) { return entityWithAuth('POST', obj); }, | |||
postWithAuth: function(obj) { return util.entityWithAuth('POST', obj); }, | |||
putWithAuth: function(obj) { return entityWithAuth('PUT', obj); }, | |||
putWithAuth: function(obj) { return util.entityWithAuth('PUT', obj); }, | |||
deleteWithAuth: function() { | |||
return { | |||
method: 'DELETE', | |||
headers: authHeader() | |||
headers: util.authHeader() | |||
}; | |||
}, | |||
@@ -116,5 +134,5 @@ export const util = { | |||
} | |||
return false; | |||
}; | |||
}, | |||
} | |||
}; |
@@ -2,6 +2,8 @@ import config from 'config'; | |||
import { util } from '../_helpers'; | |||
export const systemService = { | |||
loadIsActivated, | |||
activate, | |||
loadSystemConfigs, | |||
loadMessages, | |||
loadTimezones, | |||
@@ -9,6 +11,20 @@ export const systemService = { | |||
detectLocale | |||
}; | |||
function loadIsActivated () { | |||
const requestOptions = { method: 'GET' }; | |||
return fetch(`${config.apiUrl}/auth/activate`, requestOptions) | |||
.then(util.handleBasicResponse) | |||
.then(activated => { return activated; }); | |||
} | |||
function activate (activation) { | |||
const requestOptions = util.putNoAuth(activation); | |||
return fetch(`${config.apiUrl}/auth/activate`, requestOptions) | |||
.then(util.handleBasicResponse) | |||
.then(admin => { return admin; }); | |||
} | |||
function loadSystemConfigs() { | |||
const requestOptions = util.userLoggedIn() ? util.getWithAuth() : { method: 'GET' }; | |||
return fetch(`${config.apiUrl}/auth/configs`, requestOptions) | |||
@@ -1,12 +1,26 @@ | |||
import { userService } from '../_services'; | |||
import { userService, systemService } from '../_services'; | |||
import { router, util } from '../_helpers'; | |||
// todo: why can't we import currentUser from api-util and use that here? | |||
// when I try to do that, webpack succeeds but then an error occurs loading any page, with the | |||
// error message "_helpers.currentUser is not defined" | |||
const user = JSON.parse(localStorage.getItem('user')); | |||
// const user = JSON.parse(localStorage.getItem('user')); | |||
const user = util.currentUser(); | |||
const defaultStatus = { | |||
loggingIn: false, | |||
loggedIn: false, | |||
registering: false, | |||
activating: false, | |||
approving: false, | |||
denying: false, | |||
authenticating: false, | |||
sendingVerification: false | |||
}; | |||
const state = { | |||
status: { loggedIn: (user !== null) }, | |||
activated: null, | |||
status: Object.assign({}, defaultStatus, {loggedIn: (user != null)}), | |||
user: user, | |||
actionStatus: {} | |||
}; | |||
@@ -122,16 +136,17 @@ const actions = { | |||
const mutations = { | |||
refreshUser(state, user) { | |||
state.status = { loggedIn: (user !== null) }; | |||
state.status.loggedIn = (user !== null); | |||
state.user = user; | |||
}, | |||
loginRequest(state, user) { | |||
state.status = { loggingIn: true }; | |||
state.status.loggingIn = true; | |||
state.user = user; | |||
}, | |||
loginSuccess(state, user) { | |||
if (user.token) { | |||
state.status = { loggedIn: true }; | |||
state.status.loggingIn = false; | |||
state.status.loggedIn = true; | |||
} else if (user.multifactorAuth) { | |||
state.status = { multifactorAuth: true }; | |||
} else { | |||
@@ -141,51 +156,60 @@ const mutations = { | |||
state.user = user; | |||
}, | |||
loginFailure(state) { | |||
state.status = {}; | |||
state.status.loggingIn = false; | |||
state.user = null; | |||
}, | |||
logout(state) { | |||
state.status = {}; | |||
state.status = Object.assign({}, defaultStatus); | |||
state.user = null; | |||
}, | |||
registerRequest(state, user) { | |||
state.status = { registering: true }; | |||
state.status.registering = true; | |||
state.user = user; | |||
}, | |||
registerSuccess(state, user) { | |||
state.status = {}; | |||
state.status.registering = false; | |||
state.user = user; | |||
}, | |||
registerFailure(state) { | |||
state.status.registering = false; | |||
state.status = {}; | |||
}, | |||
approveActionRequest(state) { | |||
state.status.approving = true; | |||
state.actionStatus = { requesting: true, type: 'approve' }; | |||
}, | |||
approveActionSuccess(state, user) { | |||
state.status.approving = false; | |||
state.actionStatus = { success: true, type: 'approve', result: user }; | |||
if (user.token) state.user = user; | |||
}, | |||
approveActionFailure(state, error) { | |||
state.status.approving = false; | |||
state.actionStatus = { error: error, type: 'approve' }; | |||
}, | |||
denyActionRequest(state) { | |||
state.status.denying = true; | |||
state.actionStatus = { requesting: true, type: 'deny' }; | |||
}, | |||
denyActionSuccess(state, denial) { | |||
state.status.denying = false; | |||
state.actionStatus = { success: true, type: 'deny', result: denial }; | |||
state.denial = denial; | |||
}, | |||
denyActionFailure(state, error) { | |||
state.status.denying = false; | |||
state.actionStatus = { error: error, type: 'deny' }; | |||
}, | |||
sendAuthenticatorCodeRequest(state) { | |||
state.status.authenticating = true; | |||
state.actionStatus = { requesting: true, type: 'approve' }; | |||
}, | |||
sendAuthenticatorCodeSuccess(state, user) { | |||
state.status.authenticating = false; | |||
console.log("sendAuthenticatorCodeSuccess: user="+JSON.stringify(user)); | |||
state.actionStatus = { success: true, type: 'approve', result: user }; | |||
if (user.token) { | |||
@@ -196,17 +220,21 @@ const mutations = { | |||
} | |||
}, | |||
sendAuthenticatorCodeFailure(state, error) { | |||
state.status.authenticating = false; | |||
state.actionStatus = { error: error, type: 'approve' }; | |||
}, | |||
resendVerificationCodeRequest(state) { | |||
state.status.sendingVerification = true; | |||
state.actionStatus = { requesting: true, type: 'verify' }; | |||
}, | |||
resendVerificationCodeSuccess(state, policy) { | |||
state.status.sendingVerification = false; | |||
console.log("resendVerificationCodeSuccess: policy="+JSON.stringify(policy)); | |||
state.actionStatus = { success: true, type: 'verify', result: policy }; | |||
}, | |||
resendVerificationCodeFailure(state, error) { | |||
state.status.sendingVerification = false; | |||
state.actionStatus = { error: error, type: 'verify' }; | |||
} | |||
}; | |||
@@ -7,6 +7,7 @@ const state = { | |||
paymentsEnabled: false, | |||
sageLauncher: false | |||
}, | |||
activated: null, | |||
error: null, | |||
messages: { | |||
durationToMillis: function(count, units) { | |||
@@ -35,6 +36,15 @@ const state = { | |||
}; | |||
const actions = { | |||
loadIsActivated({ commit }) { | |||
commit('loadIsActivatedRequest'); | |||
systemService.loadIsActivated() | |||
.then( | |||
activated => commit('loadIsActivatedSuccess', activated), | |||
error => commit('loadIsActivatedFailure', error) | |||
); | |||
}, | |||
loadSystemConfigs({ commit }) { | |||
commit('loadSystemConfigsRequest'); | |||
systemService.loadSystemConfigs() | |||
@@ -150,6 +160,32 @@ const messageNotFoundHandler = { | |||
}; | |||
const mutations = { | |||
loadIsActivatedRequest(state) { | |||
console.log('loadIsActivatedRequest: starting'); | |||
}, | |||
loadIsActivatedSuccess(state, activated) { | |||
console.log('loadIsActivatedSuccess: received '+activated); | |||
state.activated = activated; | |||
}, | |||
loadIsActivatedFailure(state, error) { | |||
console.log('loadIsActivatedFailure: failed: '+error); | |||
state.errors.activated = error; | |||
}, | |||
activateRequest(state) { | |||
state.status.activating = true; | |||
}, | |||
activateSuccess(state, admin) { | |||
state.status.activating = false; | |||
state.activated = true; | |||
state.user = admin; | |||
state.status = { loggedIn: (admin !== null) }; | |||
}, | |||
activateFailure(state, error) { | |||
state.status.activating = false; | |||
state.errors.activated = error; | |||
}, | |||
loadSystemConfigsRequest(state) {}, | |||
loadSystemConfigsSuccess(state, configs) { | |||
state.configs = configs; | |||
@@ -19,7 +19,7 @@ export default { | |||
name: 'app', | |||
computed: { | |||
...mapState('account', ['status']), | |||
...mapState('system', ['configs', 'messages', 'menu']), | |||
...mapState('system', ['activated', 'configs', 'messages', 'menu']), | |||
...mapGetters('system', ['menu']), | |||
...mapState({ | |||
alert: state => state.alert | |||
@@ -27,15 +27,20 @@ export default { | |||
}, | |||
methods: { | |||
...mapActions({ clearAlert: 'alert/clear' }), | |||
...mapActions('system', ['loadSystemConfigs', 'loadMessages', 'loadTimezones']) | |||
...mapActions('system', ['loadIsActivated', 'loadSystemConfigs', 'loadMessages', 'loadTimezones']) | |||
}, | |||
watch: { | |||
$route (to, from){ | |||
// clear alert on location change | |||
this.clearAlert(); | |||
}, | |||
activated (active) { | |||
console.log('App.watch.activated: received: '+active); | |||
if (!active) this.$router.replace('/activate'); | |||
} | |||
}, | |||
created() { | |||
this.loadIsActivated(); | |||
this.loadSystemConfigs(); // determine if we can show the registration link | |||
// todo: allow user to choose locale | |||
@@ -0,0 +1,52 @@ | |||
<template> | |||
<div> | |||
Activation Page | |||
</div> | |||
</template> | |||
<script> | |||
import { mapState, mapActions, mapGetters } from 'vuex' | |||
const DNS_TEMPLATES = [ | |||
{ | |||
name: 'GoDaddyDns', | |||
driverClass: 'bubble.cloud.dns.godaddy.GoDaddyDnsDriver', | |||
credentials: { | |||
params: [ | |||
{name: 'GODADDY_API_KEY', value: null}, | |||
{name: 'GODADDY_API_SECRET', value: null}, | |||
] | |||
} | |||
} | |||
]; | |||
export default { | |||
data() { | |||
return { | |||
name: 'root', | |||
password: null, | |||
description: 'root user', | |||
networkName: null, | |||
dns: DNS_TEMPLATES[0], | |||
domain: { | |||
'name': null | |||
}, | |||
storage: {} | |||
}; | |||
}, | |||
computed: { | |||
...mapState('system', ['activated']) | |||
}, | |||
created () { | |||
}, | |||
methods: { | |||
...mapActions('system', ['loadIsActivated']) | |||
}, | |||
watch: { | |||
activated (active) { | |||
console.log('ActivationPage.watch.activated: received: '+active); | |||
if (active) this.$router.replace('/'); | |||
} | |||
} | |||
}; | |||
</script> |