From 83e994c11fcc25a525e604bf7cc100f574794e02 Mon Sep 17 00:00:00 2001
From: Christoph Wurst <christoph@winzerhof-wurst.at>
Date: Thu, 11 Oct 2018 12:20:18 +0200
Subject: Make it possible to enforce mandatory 2FA for groups

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
---
 settings/src/components/AdminTwoFactor.vue | 106 +++++++++++++++++++++++------
 1 file changed, 85 insertions(+), 21 deletions(-)

(limited to 'settings/src/components/AdminTwoFactor.vue')

diff --git a/settings/src/components/AdminTwoFactor.vue b/settings/src/components/AdminTwoFactor.vue
index a4ce482f70c..e12ceb31533 100644
--- a/settings/src/components/AdminTwoFactor.vue
+++ b/settings/src/components/AdminTwoFactor.vue
@@ -1,7 +1,7 @@
 <template>
 	<div>
 		<p>
-			{{ t('settings', 'Two-factor authentication can be enforced for all users. If they do not have a two-factor provider configured, they will be unable to log into the system.') }}
+			{{ t('settings', 'Two-factor authentication can be enforced for all	users and specific groups. If they do not have a two-factor provider configured, they will be unable to log into the system.') }}
 		</p>
 		<p v-if="loading">
 			<span class="icon-loading-small two-factor-loading"></span>
@@ -11,22 +11,74 @@
 			<input type="checkbox"
 				   id="two-factor-enforced"
 				   class="checkbox"
-				   v-model="enabled"
-				   v-on:change="onEnforcedChanged">
+				   v-model="state.enforced"
+				   v-on:change="saveChanges">
 			<label for="two-factor-enforced">{{ t('settings', 'Enforce two-factor authentication') }}</label>
 		</p>
+		<h3>{{ t('settings', 'Limit to groups') }}</h3>
+		{{ t('settings', 'Enforcement of two-factor authentication can be set for certain groups only.') }}
+		<p>
+			{{ t('settings', 'Two-factor authentication is enforced for all	members of the following groups.') }}
+		</p>
+		<p>
+			<Multiselect v-model="state.enforcedGroups"
+						 :options="groups"
+						 :placeholder="t('settings', 'Enforced groups')"
+						 :disabled="loading"
+						 :multiple="true"
+						 :searchable="true"
+						 @search-change="searchGroup"
+						 :loading="loadingGroups"
+						 :show-no-options="false"
+						 :close-on-select="false">
+			</Multiselect>
+		</p>
+		<p>
+			{{ t('settings', 'Two-factor authentication is not enforced for	members of the following groups.') }}
+		</p>
+		<p>
+			<Multiselect v-model="state.excludedGroups"
+						 :options="groups"
+						 :placeholder="t('settings', 'Excluded groups')"
+						 :disabled="loading"
+						 :multiple="true"
+						 :searchable="true"
+						 @search-change="searchGroup"
+						 :loading="loadingGroups"
+						 :show-no-options="false"
+						 :close-on-select="false">
+			</Multiselect>
+		</p>
+		<p>
+			<button class="button primary"
+					v-on:click="saveChanges"
+					:disabled="loading">
+				{{ t('settings', 'Save changes') }}
+			</button>
+		</p>
 	</div>
 </template>
 
 <script>
 	import Axios from 'nextcloud-axios'
+	import {Multiselect} from 'nextcloud-vue'
+	import _ from 'lodash'
 
 	export default {
 		name: "AdminTwoFactor",
+		components: {
+			Multiselect
+		},
 		data () {
 			return {
-				enabled: false,
-				loading: false
+				state: {
+					enforced: false,
+					enforcedGroups: [],
+					excludedGroups: [],
+				},
+				loading: false,
+				groups: [],
+				loadingGroups: false,
 			}
 		},
 		mounted () {
@@ -34,33 +86,45 @@
 			Axios.get(OC.generateUrl('/settings/api/admin/twofactorauth'))
 				.then(resp => resp.data)
 				.then(state => {
-					this.enabled = state.enabled
+					this.state = state
+
+					// Groups are loaded dynamically, but the assigned ones *should*
+					// be valid groups, so let's add them as initial state
+					this.groups = _.sortedUniq(this.state.enforcedGroups.concat(this.state.excludedGroups))
+
 					this.loading = false
-					console.info('loaded')
 				})
 				.catch(err => {
-					console.error(error)
-					this.loading = false
+					console.error('Could not load two-factor state', err)
 					throw err
 				})
 		},
 		methods: {
-			onEnforcedChanged () {
+			searchGroup: _.debounce(function (query) {
+				this.loadingGroups = true
+				Axios.get(OC.linkToOCS(`cloud/groups?offset=0&search=${encodeURIComponent(query)}&limit=20`, 2))
+					.then(res => res.data.ocs)
+					.then(ocs => ocs.data.groups)
+					.then(groups => this.groups = _.sortedUniq(this.groups.concat(groups)))
+					.catch(err => console.error('could not search groups', err))
+					.then(() => this.loadingGroups = false)
+			}, 500),
+
+			saveChanges () {
 				this.loading = true
-				const data = {
-					enabled: this.enabled
-				}
-				Axios.put(OC.generateUrl('/settings/api/admin/twofactorauth'), data)
+
+				const oldState = this.state
+
+				Axios.put(OC.generateUrl('/settings/api/admin/twofactorauth'), this.state)
 					.then(resp => resp.data)
-					.then(state => {
-						this.enabled = state.enabled
-						this.loading = false
-					})
+					.then(state => this.state = state)
 					.catch(err => {
-						console.error(error)
-						this.loading = false
-						throw err
+						console.error('could not save changes', err)
+
+						// Restore
+						this.state = oldState
 					})
+					.then(() => this.loading = false)
 			}
 		}
 	}
-- 
cgit v1.2.3

