Browse Source

feat: Disable password reset with OIDC

Owen Diffey 1 month ago
parent
commit
594c30c40d

+ 39 - 1
backend/logic/actions/users.js

@@ -565,6 +565,12 @@ export default {
 
 		return async.waterfall(
 			[
+				next => {
+					if (config.get("apis.oidc.enabled")) return next("Confirming passwords is disabled.");
+
+					return next();
+				},
+
 				next => {
 					if (!password || password === "") return next("Please provide a valid password.");
 					return next();
@@ -1823,6 +1829,12 @@ export default {
 
 		async.waterfall(
 			[
+				next => {
+					if (config.get("apis.oidc.enabled")) return next("Updating password is disabled.");
+
+					return next();
+				},
+
 				next => {
 					userModel.findOne({ _id: session.userId }, next);
 				},
@@ -1906,8 +1918,13 @@ export default {
 		async.waterfall(
 			[
 				next => {
-					if (!config.get("mail.enabled")) return next("Password resets are disabled.");
+					if (!config.get("mail.enabled") || config.get("apis.oidc.enabled"))
+						return next("Password resets are disabled.");
 
+					return next();
+				},
+
+				next => {
 					if (!email || typeof email !== "string") return next("Invalid email.");
 					email = email.toLowerCase();
 					return userModel.findOne({ "email.address": email }, next);
@@ -1985,6 +2002,13 @@ export default {
 
 			async.waterfall(
 				[
+					next => {
+						if (!config.get("mail.enabled") || config.get("apis.oidc.enabled"))
+							return next("Password resets are disabled.");
+
+						return next();
+					},
+
 					next => userModel.findOne({ _id: userId }, next),
 
 					(user, next) => {
@@ -2050,6 +2074,13 @@ export default {
 		const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
 		async.waterfall(
 			[
+				next => {
+					if (!config.get("mail.enabled") || config.get("apis.oidc.enabled"))
+						return next("Password resets are disabled.");
+
+					return next();
+				},
+
 				next => {
 					if (!code || typeof code !== "string") return next("Invalid code.");
 					return userModel.findOne({ "services.password.reset.code": code }, next);
@@ -2089,6 +2120,13 @@ export default {
 		const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
 		async.waterfall(
 			[
+				next => {
+					if (!config.get("mail.enabled") || config.get("apis.oidc.enabled"))
+						return next("Password resets are disabled.");
+
+					return next();
+				},
+
 				next => {
 					if (!code || typeof code !== "string") return next("Invalid code.");
 					return userModel.findOne({ "services.password.reset.code": code }, next);

+ 1 - 1
backend/logic/db/index.js

@@ -446,7 +446,7 @@ class _DBModule extends CoreClass {
 
 					// If a filter or property exists for a special property, add some custom pipeline steps
 					(pipeline, next) => {
-						const { properties, queries, specialProperties } = payload;
+						const { properties, queries, specialProperties = {} } = payload;
 
 						async.eachLimit(
 							Object.entries(specialProperties),

+ 1 - 1
backend/logic/hooks/hasPermission.js

@@ -63,7 +63,7 @@ permissions.moderator = {
 	"stations.remove": false,
 	"users.get": true,
 	"users.ban": true,
-	"users.requestPasswordReset": config.get("mail.enabled"),
+	"users.requestPasswordReset": config.get("mail.enabled") && !config.get("apis.oidc.enabled"),
 	"users.resendVerifyEmail": config.get("mail.enabled"),
 	"users.update": true,
 	"youtube.requestSetAdmin": true,

+ 1 - 0
backend/logic/ws.js

@@ -585,6 +585,7 @@ class _WSModule extends CoreClass {
 						config.get("registrationDisabled") === true || config.get("apis.oidc.enabled") === true,
 					mailEnabled: config.get("mail.enabled"),
 					discogsEnabled: config.get("apis.discogs.enabled"),
+					passwordResetEnabled: config.get("mail.enabled") && !config.get("apis.oidc.enabled"),
 					experimental: {
 						changable_listen_mode: config.get("experimental.changable_listen_mode"),
 						media_session: config.get("experimental.media_session"),

+ 1 - 1
frontend/src/main.ts

@@ -137,7 +137,7 @@ const router = createRouter({
 			path: "/reset_password",
 			component: () => import("@/pages/ResetPassword.vue"),
 			meta: {
-				configRequired: "mailEnabled"
+				configRequired: "passwordResetEnabled"
 			}
 		},
 		{

+ 2 - 17
frontend/src/pages/Settings/Tabs/Account.vue

@@ -7,7 +7,6 @@ import { useWebsocketsStore } from "@/stores/websockets";
 import { useUserAuthStore } from "@/stores/userAuth";
 import { useModalsStore } from "@/stores/modals";
 import _validation from "@/validation";
-import { useConfigStore } from "@/stores/config";
 
 const InputHelpBox = defineAsyncComponent(
 	() => import("@/components/InputHelpBox.vue")
@@ -21,7 +20,6 @@ const QuickConfirm = defineAsyncComponent(
 
 const settingsStore = useSettingsStore();
 const userAuthStore = useUserAuthStore();
-const configStore = useConfigStore();
 
 const { socket } = useWebsocketsStore();
 
@@ -29,7 +27,6 @@ const saveButton = ref();
 
 const { userId } = storeToRefs(userAuthStore);
 const { originalUser, modifiedUser } = settingsStore;
-const { oidcAuthentication } = storeToRefs(configStore);
 
 const validation = reactive({
 	username: {
@@ -122,8 +119,6 @@ const changeUsername = () => {
 };
 
 const saveChanges = () => {
-	if (oidcAuthentication.value) return;
-
 	const usernameChanged = modifiedUser.username !== originalUser.username;
 	const emailAddressChanged =
 		modifiedUser.email.address !== originalUser.email.address;
@@ -215,17 +210,13 @@ watch(
 				autocomplete="off"
 				@keypress="onInput('username')"
 				@paste="onInput('username')"
-				:disabled="oidcAuthentication"
 			/>
-			<span
-				v-if="modifiedUser.username && !oidcAuthentication"
-				class="character-counter"
+			<span v-if="modifiedUser.username" class="character-counter"
 				>{{ modifiedUser.username.length }}/32</span
 			>
 		</p>
 		<transition name="fadein-helpbox">
 			<input-help-box
-				v-if="!oidcAuthentication"
 				:entered="validation.username.entered"
 				:valid="validation.username.valid"
 				:message="validation.username.message"
@@ -244,23 +235,17 @@ watch(
 				@keypress="onInput('email')"
 				@paste="onInput('email')"
 				autocomplete="off"
-				:disabled="oidcAuthentication"
 			/>
 		</p>
 		<transition name="fadein-helpbox">
 			<input-help-box
-				v-if="!oidcAuthentication"
 				:entered="validation.email.entered"
 				:valid="validation.email.valid"
 				:message="validation.email.message"
 			/>
 		</transition>
 
-		<SaveButton
-			v-if="!oidcAuthentication"
-			ref="saveButton"
-			@clicked="saveChanges()"
-		/>
+		<SaveButton ref="saveButton" @clicked="saveChanges()" />
 
 		<div class="section-margin-bottom" />
 

+ 1 - 9
frontend/src/pages/Settings/Tabs/Profile.vue

@@ -6,7 +6,6 @@ import { useSettingsStore } from "@/stores/settings";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useUserAuthStore } from "@/stores/userAuth";
 import validation from "@/validation";
-import { useConfigStore } from "@/stores/config";
 
 const ProfilePicture = defineAsyncComponent(
 	() => import("@/components/ProfilePicture.vue")
@@ -17,7 +16,6 @@ const SaveButton = defineAsyncComponent(
 
 const settingsStore = useSettingsStore();
 const userAuthStore = useUserAuthStore();
-const configStore = useConfigStore();
 
 const { socket } = useWebsocketsStore();
 
@@ -25,13 +23,10 @@ const saveButton = ref();
 
 const { userId } = storeToRefs(userAuthStore);
 const { originalUser, modifiedUser } = settingsStore;
-const { oidcAuthentication } = storeToRefs(configStore);
 
 const { updateOriginalUser } = settingsStore;
 
 const changeName = () => {
-	if (oidcAuthentication.value) return null;
-
 	modifiedUser.name = modifiedUser.name.replaceAll(/ +/g, " ").trim();
 	const { name } = modifiedUser;
 
@@ -216,11 +211,8 @@ const saveChanges = () => {
 				placeholder="Enter name here..."
 				maxlength="64"
 				v-model="modifiedUser.name"
-				:disabled="oidcAuthentication"
 			/>
-			<span
-				v-if="modifiedUser.name && !oidcAuthentication"
-				class="character-counter"
+			<span v-if="modifiedUser.name" class="character-counter"
 				>{{ modifiedUser.name.length }}/64</span
 			>
 		</p>

+ 2 - 0
frontend/src/stores/config.ts

@@ -17,6 +17,7 @@ export const useConfigStore = defineStore("config", {
 		registrationDisabled: boolean;
 		mailEnabled: boolean;
 		discogsEnabled: boolean;
+		passwordResetEnabled: boolean;
 		experimental: {
 			changable_listen_mode: string[] | boolean;
 			media_session: boolean;
@@ -44,6 +45,7 @@ export const useConfigStore = defineStore("config", {
 		registrationDisabled: false,
 		mailEnabled: true,
 		discogsEnabled: true,
+		passwordResetEnabled: true,
 		experimental: {
 			changable_listen_mode: [],
 			media_session: false,