Skip to content

Commit 5a65c82

Browse files
authored
Merge pull request #100 from loneil/feature/publicOption
Enable Public forms
2 parents 8fc7b09 + 52e295a commit 5a65c82

File tree

10 files changed

+82
-24
lines changed

10 files changed

+82
-24
lines changed

app/frontend/src/components/base/BaseAuthButton.vue

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ export default {
2828
methods: {
2929
login() {
3030
if (this.keycloakReady) {
31-
window.location.replace(this.createLoginUrl());
32-
//window.location.replace(this.createLoginUrl({ idpHint: 'idir' }));
31+
window.location.replace(this.createLoginUrl({ idpHint: 'idir' }));
3332
}
3433
},
3534
logout() {

app/frontend/src/components/base/BaseSecure.vue

+1-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ export default {
5353
methods: {
5454
login() {
5555
if (this.keycloakReady) {
56-
window.location.replace(this.createLoginUrl());
57-
// window.location.replace(this.createLoginUrl({ idpHint: 'idir' }));
56+
window.location.replace(this.createLoginUrl({ idpHint: 'idir' }));
5857
}
5958
},
6059
},

app/frontend/src/components/designer/FormSettings.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
:mandatory="false"
3636
:rules="loginRequiredRules"
3737
>
38-
<v-radio disabled label="Public (anonymous)" :value="ID_MODE.PUBLIC" />
38+
<v-radio label="Public (anonymous)" :value="ID_MODE.PUBLIC" />
3939
<v-radio label="Log-in Required" value="login"></v-radio>
4040
<v-row v-if="userType === ID_MODE.LOGIN" class="pl-6 mb-2">
4141
<v-checkbox

app/frontend/src/router/index.js

+25-13
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,19 @@ export default function getRouter(basePath = '/') {
4747
name: 'FormCreate',
4848
component: () => import(/* webpackChunkName: "create" */ '@/views/form/Create.vue'),
4949
meta: {
50-
breadcrumbTitle: 'Form Designer'
50+
breadcrumbTitle: 'Form Designer',
51+
requiresAuth: true,
52+
hasLogin: true
5153
},
5254
},
5355
{
5456
path: 'design',
5557
name: 'FormDesigner',
5658
component: () => import(/* webpackChunkName: "designer" */ '@/views/form/Design.vue'),
5759
meta: {
58-
breadcrumbTitle: 'Form Designer'
60+
breadcrumbTitle: 'Form Designer',
61+
requiresAuth: true,
62+
hasLogin: true
5963
},
6064
props: createProps
6165
},
@@ -64,7 +68,9 @@ export default function getRouter(basePath = '/') {
6468
name: 'FormManage',
6569
component: () => import(/* webpackChunkName: "manage" */ '@/views/form/Manage.vue'),
6670
meta: {
67-
breadcrumbTitle: 'Manage Form'
71+
breadcrumbTitle: 'Manage Form',
72+
requiresAuth: true,
73+
hasLogin: true
6874
},
6975
props: createProps
7076
},
@@ -73,7 +79,9 @@ export default function getRouter(basePath = '/') {
7379
name: 'FormPreview',
7480
component: () => import(/* webpackChunkName: "viewsubmission" */ '@/views/form/Preview.vue'),
7581
meta: {
76-
breadcrumbTitle: 'Preview Form'
82+
breadcrumbTitle: 'Preview Form',
83+
requiresAuth: true,
84+
hasLogin: true
7785
},
7886
props: createProps
7987
},
@@ -82,7 +90,9 @@ export default function getRouter(basePath = '/') {
8290
name: 'FormSubmissions',
8391
component: () => import(/* webpackChunkName: "submissions" */ '@/views/form/Submissions.vue'),
8492
meta: {
85-
breadcrumbTitle: 'Submissions'
93+
breadcrumbTitle: 'Submissions',
94+
requiresAuth: true,
95+
hasLogin: true
8696
},
8797
props: createProps
8898
},
@@ -111,7 +121,9 @@ export default function getRouter(basePath = '/') {
111121
name: 'FormTeams',
112122
component: () => import(/* webpackChunkName: "teams" */ '@/views/form/Teams.vue'),
113123
meta: {
114-
breadcrumbTitle: 'Team Management'
124+
breadcrumbTitle: 'Team Management',
125+
requiresAuth: true,
126+
hasLogin: true
115127
},
116128
props: createProps
117129
},
@@ -120,15 +132,13 @@ export default function getRouter(basePath = '/') {
120132
name: 'FormView',
121133
component: () => import(/* webpackChunkName: "viewsubmission" */ '@/views/form/View.vue'),
122134
meta: {
123-
breadcrumbTitle: 'View Submission'
135+
breadcrumbTitle: 'View Submission',
136+
requiresAuth: true,
137+
hasLogin: true
124138
},
125139
props: createProps
126140
},
127-
],
128-
meta: {
129-
requiresAuth: true,
130-
hasLogin: true
131-
}
141+
]
132142
},
133143
{
134144
path: '/user',
@@ -193,13 +203,15 @@ export default function getRouter(basePath = '/') {
193203
}
194204

195205
// Force login redirect if not authenticated
206+
// Note some pages (Submit and Success) only require auth if the form being loaded is secured
207+
// in those cases, see the navigation gaurds in their views for auth loop
196208
if (to.matched.some(route => route.meta.requiresAuth)
197209
&& router.app.$keycloak
198210
&& router.app.$keycloak.ready
199211
&& !router.app.$keycloak.authenticated) {
200212
const redirect = location.origin + basePath + to.path + location.search;
201213
const loginUrl = router.app.$keycloak.createLoginUrl({
202-
//idpHint: 'idir',
214+
idpHint: 'idir',
203215
redirectUri: redirect
204216
});
205217
window.location.replace(loginUrl);

app/frontend/src/store/modules/auth.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default {
1818
authenticated: () => Vue.prototype.$keycloak.authenticated,
1919
createLoginUrl: () => options => Vue.prototype.$keycloak.createLoginUrl(options),
2020
createLogoutUrl: () => options => Vue.prototype.$keycloak.createLogoutUrl(options),
21-
email: () => Vue.prototype.$keycloak.tokenParsed.email,
21+
email: () => Vue.prototype.$keycloak.tokenParsed ? Vue.prototype.$keycloak.tokenParsed.email : '',
2222
fullName: () => Vue.prototype.$keycloak.fullName,
2323
hasResourceRoles: (_state, getters) => (resource, roles) => {
2424
if (!getters.authenticated) return false;

app/frontend/src/utils/permissionUtils.js

+40
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { FormPermissions, IdentityProviders } from '@/utils/constants';
2+
import { formService } from '@/services';
3+
import store from '@/store';
24

35
//
46
// Utility Functions for determining permissions
@@ -45,3 +47,41 @@ export function checkSubmissionView(userForm) {
4547
];
4648
return userForm && userForm.permissions && userForm.permissions.some(p => perms.includes(p));
4749
}
50+
51+
/**
52+
* @function determineFormNeedsAuth
53+
* When loading a form to fill out, determine if the user needs to log in to submit it
54+
* @param {Object} store The vuex store reference
55+
* @param {String} formId The form guid
56+
* @param {String} submissionId The submission guid
57+
* @param {Object} next The routing next object
58+
*/
59+
export async function determineFormNeedsAuth(formId, submissionId, next) {
60+
// before this view is loaded, determine if this is a public form
61+
// if it IS, they don't need to log in. If it's secured, go through auth loop
62+
// If authed already skip all this
63+
if (!store.getters['auth/authenticated']) {
64+
try {
65+
// Get this form or submission
66+
if (formId) {
67+
await formService.readForm(formId);
68+
} else if (submissionId) {
69+
await formService.getSubmission(submissionId);
70+
}
71+
} catch (error) {
72+
// If there's a 401 trying to get this form, make that user log in
73+
if (error.response && error.response.status === 401) {
74+
window.location.replace(
75+
store.getters['auth/createLoginUrl']({ idpHint: 'idir' })
76+
);
77+
} else {
78+
// Other errors raise an issue
79+
store.dispatch('notifications/addNotification', {
80+
message: 'An error occurred while loading this form.',
81+
consoleError: `Error loading form ${formId} or submission ${submissionId}: ${error}`,
82+
});
83+
}
84+
}
85+
}
86+
next();
87+
}

app/frontend/src/utils/transformUtils.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function generateIdps({ idps, userType }) {
1616
if (userType === IdentityMode.LOGIN && idps && idps.length) {
1717
identityProviders = identityProviders.concat(idps.map((i) => ({ code: i })));
1818
} else if (userType === IdentityMode.PUBLIC) {
19-
identityProviders.push(IdentityMode.PUBLIC);
19+
identityProviders.push({ code: IdentityMode.PUBLIC });
2020
}
2121
return identityProviders;
2222
}
@@ -33,7 +33,7 @@ export function parseIdps(identityProviders) {
3333
userType: IdentityMode.TEAM,
3434
};
3535
if (identityProviders && identityProviders.length) {
36-
if (identityProviders[0] === IdentityMode.PUBLIC) {
36+
if (identityProviders[0].code === IdentityMode.PUBLIC) {
3737
result.userType = IdentityMode.PUBLIC;
3838
} else {
3939
result.userType = IdentityMode.LOGIN;

app/frontend/src/views/form/Submit.vue

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</template>
66

77
<script>
8+
import { determineFormNeedsAuth } from '@/utils/permissionUtils';
89
import FormViewer from '@/components/designer/FormViewer.vue';
910
1011
export default {
@@ -13,7 +14,10 @@ export default {
1314
FormViewer,
1415
},
1516
props: {
16-
f: String
17+
f: String,
18+
},
19+
beforeRouteEnter(to, from, next) {
20+
determineFormNeedsAuth(to.query.f, undefined, next);
1721
},
1822
};
1923
</script>

app/frontend/src/views/form/Success.vue

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<script>
3030
import { mapGetters } from 'vuex';
3131
32+
import { determineFormNeedsAuth } from '@/utils/permissionUtils';
3233
import FormViewer from '@/components/designer/FormViewer.vue';
3334
import RequestReceipt from '@/components/forms/RequestReceipt.vue';
3435
@@ -42,5 +43,8 @@ export default {
4243
RequestReceipt,
4344
},
4445
computed: mapGetters('auth', ['email']),
46+
beforeRouteEnter(to, from, next) {
47+
determineFormNeedsAuth(undefined, to.query.s, next);
48+
},
4549
};
4650
</script>

app/frontend/tests/unit/utils/transformUtils.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('generateIdps', () => {
2121
});
2222

2323
it('returns correct values when usertype is public', () => {
24-
expect(transformUtils.generateIdps({ userType: IdentityMode.PUBLIC })).toEqual([IdentityMode.PUBLIC]);
24+
expect(transformUtils.generateIdps({ userType: IdentityMode.PUBLIC })).toEqual([{ code: IdentityMode.PUBLIC }]);
2525
});
2626
});
2727

@@ -41,7 +41,7 @@ describe('parseIdps', () => {
4141
});
4242

4343
it('returns an empty array idps and usertype public when public', () => {
44-
expect(transformUtils.parseIdps([IdentityMode.PUBLIC])).toEqual({
44+
expect(transformUtils.parseIdps([{ code: IdentityMode.PUBLIC }])).toEqual({
4545
idps: [],
4646
userType: IdentityMode.PUBLIC,
4747
});

0 commit comments

Comments
 (0)