diff --git a/app/frontend/src/components/base/BaseAuthButton.vue b/app/frontend/src/components/base/BaseAuthButton.vue index 8b77ba3f9..944b26f75 100755 --- a/app/frontend/src/components/base/BaseAuthButton.vue +++ b/app/frontend/src/components/base/BaseAuthButton.vue @@ -28,8 +28,7 @@ export default { methods: { login() { if (this.keycloakReady) { - window.location.replace(this.createLoginUrl()); - //window.location.replace(this.createLoginUrl({ idpHint: 'idir' })); + window.location.replace(this.createLoginUrl({ idpHint: 'idir' })); } }, logout() { diff --git a/app/frontend/src/components/base/BaseSecure.vue b/app/frontend/src/components/base/BaseSecure.vue index eab96d49d..40469f3bc 100755 --- a/app/frontend/src/components/base/BaseSecure.vue +++ b/app/frontend/src/components/base/BaseSecure.vue @@ -53,8 +53,7 @@ export default { methods: { login() { if (this.keycloakReady) { - window.location.replace(this.createLoginUrl()); - // window.location.replace(this.createLoginUrl({ idpHint: 'idir' })); + window.location.replace(this.createLoginUrl({ idpHint: 'idir' })); } }, }, diff --git a/app/frontend/src/components/designer/FormSettings.vue b/app/frontend/src/components/designer/FormSettings.vue index 3cc9b6935..582a2b30d 100644 --- a/app/frontend/src/components/designer/FormSettings.vue +++ b/app/frontend/src/components/designer/FormSettings.vue @@ -35,7 +35,7 @@ :mandatory="false" :rules="loginRequiredRules" > - + import(/* webpackChunkName: "create" */ '@/views/form/Create.vue'), meta: { - breadcrumbTitle: 'Form Designer' + breadcrumbTitle: 'Form Designer', + requiresAuth: true, + hasLogin: true }, }, { @@ -55,7 +57,9 @@ export default function getRouter(basePath = '/') { name: 'FormDesigner', component: () => import(/* webpackChunkName: "designer" */ '@/views/form/Design.vue'), meta: { - breadcrumbTitle: 'Form Designer' + breadcrumbTitle: 'Form Designer', + requiresAuth: true, + hasLogin: true }, props: createProps }, @@ -64,7 +68,9 @@ export default function getRouter(basePath = '/') { name: 'FormManage', component: () => import(/* webpackChunkName: "manage" */ '@/views/form/Manage.vue'), meta: { - breadcrumbTitle: 'Manage Form' + breadcrumbTitle: 'Manage Form', + requiresAuth: true, + hasLogin: true }, props: createProps }, @@ -73,7 +79,9 @@ export default function getRouter(basePath = '/') { name: 'FormPreview', component: () => import(/* webpackChunkName: "viewsubmission" */ '@/views/form/Preview.vue'), meta: { - breadcrumbTitle: 'Preview Form' + breadcrumbTitle: 'Preview Form', + requiresAuth: true, + hasLogin: true }, props: createProps }, @@ -82,7 +90,9 @@ export default function getRouter(basePath = '/') { name: 'FormSubmissions', component: () => import(/* webpackChunkName: "submissions" */ '@/views/form/Submissions.vue'), meta: { - breadcrumbTitle: 'Submissions' + breadcrumbTitle: 'Submissions', + requiresAuth: true, + hasLogin: true }, props: createProps }, @@ -111,7 +121,9 @@ export default function getRouter(basePath = '/') { name: 'FormTeams', component: () => import(/* webpackChunkName: "teams" */ '@/views/form/Teams.vue'), meta: { - breadcrumbTitle: 'Team Management' + breadcrumbTitle: 'Team Management', + requiresAuth: true, + hasLogin: true }, props: createProps }, @@ -120,15 +132,13 @@ export default function getRouter(basePath = '/') { name: 'FormView', component: () => import(/* webpackChunkName: "viewsubmission" */ '@/views/form/View.vue'), meta: { - breadcrumbTitle: 'View Submission' + breadcrumbTitle: 'View Submission', + requiresAuth: true, + hasLogin: true }, props: createProps }, - ], - meta: { - requiresAuth: true, - hasLogin: true - } + ] }, { path: '/user', @@ -193,13 +203,15 @@ export default function getRouter(basePath = '/') { } // Force login redirect if not authenticated + // Note some pages (Submit and Success) only require auth if the form being loaded is secured + // in those cases, see the navigation gaurds in their views for auth loop if (to.matched.some(route => route.meta.requiresAuth) && router.app.$keycloak && router.app.$keycloak.ready && !router.app.$keycloak.authenticated) { const redirect = location.origin + basePath + to.path + location.search; const loginUrl = router.app.$keycloak.createLoginUrl({ - //idpHint: 'idir', + idpHint: 'idir', redirectUri: redirect }); window.location.replace(loginUrl); diff --git a/app/frontend/src/store/modules/auth.js b/app/frontend/src/store/modules/auth.js index 871562698..418fe8f10 100755 --- a/app/frontend/src/store/modules/auth.js +++ b/app/frontend/src/store/modules/auth.js @@ -18,7 +18,7 @@ export default { authenticated: () => Vue.prototype.$keycloak.authenticated, createLoginUrl: () => options => Vue.prototype.$keycloak.createLoginUrl(options), createLogoutUrl: () => options => Vue.prototype.$keycloak.createLogoutUrl(options), - email: () => Vue.prototype.$keycloak.tokenParsed.email, + email: () => Vue.prototype.$keycloak.tokenParsed ? Vue.prototype.$keycloak.tokenParsed.email : '', fullName: () => Vue.prototype.$keycloak.fullName, hasResourceRoles: (_state, getters) => (resource, roles) => { if (!getters.authenticated) return false; diff --git a/app/frontend/src/utils/permissionUtils.js b/app/frontend/src/utils/permissionUtils.js index 197569886..102377fa1 100644 --- a/app/frontend/src/utils/permissionUtils.js +++ b/app/frontend/src/utils/permissionUtils.js @@ -1,4 +1,6 @@ import { FormPermissions, IdentityProviders } from '@/utils/constants'; +import { formService } from '@/services'; +import store from '@/store'; // // Utility Functions for determining permissions @@ -45,3 +47,41 @@ export function checkSubmissionView(userForm) { ]; return userForm && userForm.permissions && userForm.permissions.some(p => perms.includes(p)); } + +/** + * @function determineFormNeedsAuth + * When loading a form to fill out, determine if the user needs to log in to submit it + * @param {Object} store The vuex store reference + * @param {String} formId The form guid + * @param {String} submissionId The submission guid + * @param {Object} next The routing next object + */ +export async function determineFormNeedsAuth(formId, submissionId, next) { + // before this view is loaded, determine if this is a public form + // if it IS, they don't need to log in. If it's secured, go through auth loop + // If authed already skip all this + if (!store.getters['auth/authenticated']) { + try { + // Get this form or submission + if (formId) { + await formService.readForm(formId); + } else if (submissionId) { + await formService.getSubmission(submissionId); + } + } catch (error) { + // If there's a 401 trying to get this form, make that user log in + if (error.response && error.response.status === 401) { + window.location.replace( + store.getters['auth/createLoginUrl']({ idpHint: 'idir' }) + ); + } else { + // Other errors raise an issue + store.dispatch('notifications/addNotification', { + message: 'An error occurred while loading this form.', + consoleError: `Error loading form ${formId} or submission ${submissionId}: ${error}`, + }); + } + } + } + next(); +} diff --git a/app/frontend/src/utils/transformUtils.js b/app/frontend/src/utils/transformUtils.js index 0bee5f890..c1eb5b5e1 100644 --- a/app/frontend/src/utils/transformUtils.js +++ b/app/frontend/src/utils/transformUtils.js @@ -16,7 +16,7 @@ export function generateIdps({ idps, userType }) { if (userType === IdentityMode.LOGIN && idps && idps.length) { identityProviders = identityProviders.concat(idps.map((i) => ({ code: i }))); } else if (userType === IdentityMode.PUBLIC) { - identityProviders.push(IdentityMode.PUBLIC); + identityProviders.push({ code: IdentityMode.PUBLIC }); } return identityProviders; } @@ -33,7 +33,7 @@ export function parseIdps(identityProviders) { userType: IdentityMode.TEAM, }; if (identityProviders && identityProviders.length) { - if (identityProviders[0] === IdentityMode.PUBLIC) { + if (identityProviders[0].code === IdentityMode.PUBLIC) { result.userType = IdentityMode.PUBLIC; } else { result.userType = IdentityMode.LOGIN; diff --git a/app/frontend/src/views/form/Submit.vue b/app/frontend/src/views/form/Submit.vue index b7cec97d1..9e71fcaf6 100644 --- a/app/frontend/src/views/form/Submit.vue +++ b/app/frontend/src/views/form/Submit.vue @@ -5,6 +5,7 @@ diff --git a/app/frontend/src/views/form/Success.vue b/app/frontend/src/views/form/Success.vue index 26bdc2e5c..56c611bdd 100644 --- a/app/frontend/src/views/form/Success.vue +++ b/app/frontend/src/views/form/Success.vue @@ -29,6 +29,7 @@ diff --git a/app/frontend/tests/unit/utils/transformUtils.spec.js b/app/frontend/tests/unit/utils/transformUtils.spec.js index 413114835..cc3a8afe2 100644 --- a/app/frontend/tests/unit/utils/transformUtils.spec.js +++ b/app/frontend/tests/unit/utils/transformUtils.spec.js @@ -21,7 +21,7 @@ describe('generateIdps', () => { }); it('returns correct values when usertype is public', () => { - expect(transformUtils.generateIdps({ userType: IdentityMode.PUBLIC })).toEqual([IdentityMode.PUBLIC]); + expect(transformUtils.generateIdps({ userType: IdentityMode.PUBLIC })).toEqual([{ code: IdentityMode.PUBLIC }]); }); }); @@ -41,7 +41,7 @@ describe('parseIdps', () => { }); it('returns an empty array idps and usertype public when public', () => { - expect(transformUtils.parseIdps([IdentityMode.PUBLIC])).toEqual({ + expect(transformUtils.parseIdps([{ code: IdentityMode.PUBLIC }])).toEqual({ idps: [], userType: IdentityMode.PUBLIC, });