diff --git a/src/_helpers/router.js b/src/_helpers/router.js index e507b3e..0739e79 100644 --- a/src/_helpers/router.js +++ b/src/_helpers/router.js @@ -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(); diff --git a/src/_helpers/util.js b/src/_helpers/util.js index 5754d09..10089d1 100644 --- a/src/_helpers/util.js +++ b/src/_helpers/util.js @@ -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; }; - }, + } }; diff --git a/src/_services/system.service.js b/src/_services/system.service.js index 9aeec65..0dd17ec 100644 --- a/src/_services/system.service.js +++ b/src/_services/system.service.js @@ -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) diff --git a/src/_store/account.module.js b/src/_store/account.module.js index 10290e8..0d21176 100644 --- a/src/_store/account.module.js +++ b/src/_store/account.module.js @@ -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' }; } }; diff --git a/src/_store/system.module.js b/src/_store/system.module.js index 4a87ecb..21f87d9 100644 --- a/src/_store/system.module.js +++ b/src/_store/system.module.js @@ -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; diff --git a/src/app/App.vue b/src/app/App.vue index c066df3..afc34d8 100644 --- a/src/app/App.vue +++ b/src/app/App.vue @@ -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 diff --git a/src/auth/ActivationPage.vue b/src/auth/ActivationPage.vue new file mode 100644 index 0000000..00df7f0 --- /dev/null +++ b/src/auth/ActivationPage.vue @@ -0,0 +1,52 @@ + + + \ No newline at end of file