diff --git a/app/frontend/src/components/forms/manage/AddTeamMember.vue b/app/frontend/src/components/forms/manage/AddTeamMember.vue
index 0315a74e1..ffdca0759 100644
--- a/app/frontend/src/components/forms/manage/AddTeamMember.vue
+++ b/app/frontend/src/components/forms/manage/AddTeamMember.vue
@@ -1,12 +1,13 @@
-
+
diff --git a/app/frontend/src/views/Login.vue b/app/frontend/src/views/Login.vue
index 701c15737..4f2598a69 100644
--- a/app/frontend/src/views/Login.vue
+++ b/app/frontend/src/views/Login.vue
@@ -3,49 +3,29 @@ import { mapActions, mapState } from 'pinia';
import { useAuthStore } from '~/store/auth';
import { useFormStore } from '~/store/form';
-import { IdentityProviders } from '~/utils/constants';
+import { useIdpStore } from '~/store/identityProviders';
export default {
props: {
idpHint: {
type: Array,
- default: () => [
- IdentityProviders.IDIR,
- IdentityProviders.BCEIDBUSINESS,
- IdentityProviders.BCEIDBASIC,
- ],
+ default: () => [],
},
},
computed: {
...mapState(useAuthStore, ['authenticated', 'createLoginUrl', 'ready']),
...mapState(useFormStore, ['lang']),
- buttons: () => [
- {
- label: 'IDIR',
- type: IdentityProviders.IDIR,
- },
- {
- label: 'Basic BCeID',
- type: IdentityProviders.BCEIDBASIC,
- },
- {
- label: 'Business BCeID',
- type: IdentityProviders.BCEIDBUSINESS,
- },
- ],
- IDPS() {
- return IdentityProviders;
- },
+ ...mapState(useIdpStore, ['loginButtons', 'loginIdpHints']),
},
created() {
// If component gets idpHint, invoke login flow via vuex
- if (this.idpHint && this.idpHint.length === 1) this.login(this.idpHint[0]);
+ if (this.idpHint && this.idpHint.length === 1) {
+ const hint = this.idpHint[0];
+ if (this.loginIdpHints.includes(hint)) this.login(hint);
+ }
},
methods: {
...mapActions(useAuthStore, ['login']),
- buttonEnabled(type) {
- return this.idpHint ? this.idpHint.includes(type) : false;
- },
},
};
@@ -56,8 +36,8 @@ export default {
{{ $t('trans.login.authenticateWith') }}
-
-
+
+
IdentityProviders,
+ APP_PERMS: () => AppPermissions,
},
async mounted() {
await this.getFile(this.id);
@@ -77,7 +77,7 @@ export default {
-
+
{{ $t('trans.download.chefsDataExport') }}
IdentityProviders,
stepper() {
return this.step;
},
diff --git a/app/frontend/src/views/form/Emails.vue b/app/frontend/src/views/form/Emails.vue
index 783b65978..5f8a0eadb 100644
--- a/app/frontend/src/views/form/Emails.vue
+++ b/app/frontend/src/views/form/Emails.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/app/frontend/src/views/form/Export.vue b/app/frontend/src/views/form/Export.vue
index f9111decf..ea7842c23 100644
--- a/app/frontend/src/views/form/Export.vue
+++ b/app/frontend/src/views/form/Export.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/app/frontend/src/views/form/Manage.vue b/app/frontend/src/views/form/Manage.vue
index 3b1562d62..020e8f701 100644
--- a/app/frontend/src/views/form/Manage.vue
+++ b/app/frontend/src/views/form/Manage.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/app/frontend/src/views/form/Preview.vue b/app/frontend/src/views/form/Preview.vue
index bdf04abb2..bbe0fb327 100644
--- a/app/frontend/src/views/form/Preview.vue
+++ b/app/frontend/src/views/form/Preview.vue
@@ -4,7 +4,7 @@ import BaseSecure from '~/components/base/BaseSecure.vue';
import FormViewer from '~/components/designer/FormViewer.vue';
import { useFormStore } from '~/store/form';
-import { IdentityProviders } from '~/utils/constants';
+import { AppPermissions } from '~/utils/constants';
export default {
components: {
@@ -27,13 +27,13 @@ export default {
},
computed: {
...mapState(useFormStore, ['isRTL', 'lang']),
- IDP: () => IdentityProviders,
+ APP_PERMS: () => AppPermissions,
},
};
-
+
import BaseSecure from '~/components/base/BaseSecure.vue';
import SubmissionsTable from '~/components/forms/SubmissionsTable.vue';
-import { IdentityProviders } from '~/utils/constants';
+import { AppPermissions } from '~/utils/constants';
export default {
components: {
@@ -15,13 +15,13 @@ export default {
},
},
computed: {
- IDP: () => IdentityProviders,
+ APP_PERMS: () => AppPermissions,
},
};
-
+
diff --git a/app/frontend/src/views/form/Teams.vue b/app/frontend/src/views/form/Teams.vue
index b35d933f5..9d4bd2bc0 100644
--- a/app/frontend/src/views/form/Teams.vue
+++ b/app/frontend/src/views/form/Teams.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/app/frontend/src/views/form/View.vue b/app/frontend/src/views/form/View.vue
index 90b738cb1..bbfdc2416 100644
--- a/app/frontend/src/views/form/View.vue
+++ b/app/frontend/src/views/form/View.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/app/frontend/src/views/user/Submissions.vue b/app/frontend/src/views/user/Submissions.vue
index bb0177c10..8205d81de 100644
--- a/app/frontend/src/views/user/Submissions.vue
+++ b/app/frontend/src/views/user/Submissions.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/app/src/db/migrations/20240119172630_identity_provider_permissions.js b/app/src/db/migrations/20240119172630_identity_provider_permissions.js
new file mode 100644
index 000000000..f7aef6bd0
--- /dev/null
+++ b/app/src/db/migrations/20240119172630_identity_provider_permissions.js
@@ -0,0 +1,118 @@
+const { APP_PERMISSIONS, Roles } = require('../../forms/common/constants');
+
+const BCEID_EXTRAS = {
+ formAccessSettings: 'idim',
+ addTeamMemberSearch: {
+ text: {
+ minLength: 6,
+ message: 'trans.manageSubmissionUsers.searchInputLength',
+ },
+ email: {
+ exact: true,
+ message: 'trans.manageSubmissionUsers.exactBCEIDSearch',
+ },
+ },
+};
+
+exports.up = function (knex) {
+ return Promise.resolve().then(() =>
+ knex.schema
+ .alterTable('identity_provider', (table) => {
+ table.boolean('primary').notNullable().defaultTo(false);
+ table
+ .boolean('login')
+ .notNullable()
+ .defaultTo(false)
+ .comment('When true, supply buttons to launch login process');
+ table
+ .specificType('permissions', 'text ARRAY')
+ .comment('Map app permissions to the idp');
+ table
+ .specificType('roles', 'text ARRAY')
+ .comment('Map Form role codes to the idp');
+ table
+ .jsonb('extra')
+ .comment(
+ 'Allow customization of the IDP though extra (json) config object.'
+ );
+ })
+ .then(() =>
+ knex('identity_provider')
+ .where({ code: 'public' })
+ .update({ permissions: [], extra: {} })
+ )
+ .then(() =>
+ knex('identity_provider')
+ .where({ code: 'idir' })
+ .update({ primary: true, login: true })
+ )
+ .then(() =>
+ knex('identity_provider')
+ .where({ code: 'idir' })
+ .update({
+ permissions: [
+ APP_PERMISSIONS.VIEWS_FORM_STEPPER,
+ APP_PERMISSIONS.VIEWS_ADMIN,
+ APP_PERMISSIONS.VIEWS_FILE_DOWNLOAD,
+ APP_PERMISSIONS.VIEWS_FORM_EMAILS,
+ APP_PERMISSIONS.VIEWS_FORM_EXPORT,
+ APP_PERMISSIONS.VIEWS_FORM_MANAGE,
+ APP_PERMISSIONS.VIEWS_FORM_PREVIEW,
+ APP_PERMISSIONS.VIEWS_FORM_SUBMISSIONS,
+ APP_PERMISSIONS.VIEWS_FORM_TEAMS,
+ APP_PERMISSIONS.VIEWS_FORM_VIEW,
+ APP_PERMISSIONS.VIEWS_USER_SUBMISSIONS,
+ ],
+ roles: [
+ Roles.OWNER,
+ Roles.TEAM_MANAGER,
+ Roles.FORM_DESIGNER,
+ Roles.SUBMISSION_REVIEWER,
+ Roles.FORM_SUBMITTER,
+ ],
+ extra: {},
+ })
+ )
+ .then(() =>
+ knex('identity_provider')
+ .where({ code: 'bceid-business' })
+ .update({
+ login: true,
+ permissions: [
+ APP_PERMISSIONS.VIEWS_FORM_EXPORT,
+ APP_PERMISSIONS.VIEWS_FORM_MANAGE,
+ APP_PERMISSIONS.VIEWS_FORM_SUBMISSIONS,
+ APP_PERMISSIONS.VIEWS_FORM_TEAMS,
+ APP_PERMISSIONS.VIEWS_FORM_VIEW,
+ APP_PERMISSIONS.VIEWS_USER_SUBMISSIONS,
+ ],
+ roles: [
+ Roles.TEAM_MANAGER,
+ Roles.SUBMISSION_REVIEWER,
+ Roles.FORM_SUBMITTER,
+ ],
+ extra: BCEID_EXTRAS,
+ })
+ )
+ .then(() =>
+ knex('identity_provider')
+ .where({ code: 'bceid-basic' })
+ .update({
+ login: true,
+ permissions: [APP_PERMISSIONS.VIEWS_USER_SUBMISSIONS],
+ roles: [Roles.FORM_SUBMITTER],
+ extra: BCEID_EXTRAS,
+ })
+ )
+ );
+};
+
+exports.down = function (knex) {
+ return Promise.resolve().then(() =>
+ knex.schema.alterTable('identity_provider', (table) => {
+ table.dropColumn('primary');
+ table.dropColumn('permissions');
+ table.dropColumn('extra');
+ })
+ );
+};
diff --git a/app/src/forms/common/constants.js b/app/src/forms/common/constants.js
index 278df008a..4224db129 100644
--- a/app/src/forms/common/constants.js
+++ b/app/src/forms/common/constants.js
@@ -90,4 +90,21 @@ module.exports = Object.freeze({
json: 'json',
default: 'csv',
},
+ // app permissions are not assigned on the form
+ // they are for flow within the UX, what views one can navigate
+ // what buttons one can have.
+ // these will be assigned via the user's IDP.
+ APP_PERMISSIONS: {
+ VIEWS_FORM_STEPPER: 'views_form_stepper',
+ VIEWS_ADMIN: 'views_admin',
+ VIEWS_FILE_DOWNLOAD: 'views_file_download',
+ VIEWS_FORM_EMAILS: 'views_form_emails',
+ VIEWS_FORM_EXPORT: 'views_form_export',
+ VIEWS_FORM_MANAGE: 'views_form_manage',
+ VIEWS_FORM_PREVIEW: 'views_form_preview',
+ VIEWS_FORM_SUBMISSIONS: 'views_form_submissions',
+ VIEWS_FORM_TEAMS: 'views_form_teamS',
+ VIEWS_FORM_VIEW: 'views_form_view',
+ VIEWS_USER_SUBMISSIONS: 'views_user_submissions',
+ },
});
diff --git a/app/src/forms/common/models/tables/identityProvider.js b/app/src/forms/common/models/tables/identityProvider.js
index cebd48143..d340e4c3c 100644
--- a/app/src/forms/common/models/tables/identityProvider.js
+++ b/app/src/forms/common/models/tables/identityProvider.js
@@ -19,7 +19,7 @@ class IdentityProvider extends Timestamps(Model) {
}
},
orderDefault(builder) {
- builder.orderByRaw('lower("identity_provider"."code")');
+ builder.orderByRaw('"identity_provider"."primary" DESC NULLS LAST, lower("identity_provider"."code")');
},
};
}
@@ -33,6 +33,11 @@ class IdentityProvider extends Timestamps(Model) {
display: { type: 'string', minLength: 1, maxLength: 255 },
idp: { type: 'string', minLength: 1, maxLength: 255 },
active: { type: 'boolean' },
+ primary: { type: 'boolean' },
+ login: { type: 'boolean' },
+ permissions: { type: ['array', 'null'], items: { type: 'string' } },
+ roles: { type: ['array', 'null'], items: { type: 'string' } },
+ extra: { type: 'object' },
...stamps,
},
additionalProperties: false,