diff --git a/api/src/store/modules/config.ts b/api/src/store/modules/config.ts index 6fa919c26..81187f25a 100644 --- a/api/src/store/modules/config.ts +++ b/api/src/store/modules/config.ts @@ -1,5 +1,4 @@ import { F_OK } from 'constants'; -import { randomBytes } from 'crypto'; import { writeFileSync } from 'fs'; import { access } from 'fs/promises'; @@ -24,7 +23,6 @@ import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access'; import { FileLoadStatus } from '@app/store/types'; import { type RecursivePartial } from '@app/types'; import { type MyServersConfig, type MyServersConfigMemory } from '@app/types/my-servers-config'; -import { isFulfilled } from '@app/utils'; export type SliceState = { status: FileLoadStatus; @@ -219,7 +217,18 @@ export const config = createSlice({ const stateAsArray = state.remote.ssoSubIds.split(','); stateAsArray.push(action.payload); state.remote.ssoSubIds = stateAsArray.join(','); - } + }, + removeSsoUser(state, action: PayloadAction) { + if (action.payload === null) { + state.remote.ssoSubIds = ''; + return; + } + if (!state.remote.ssoSubIds.includes(action.payload)) { + return; + } + const stateAsArray = state.remote.ssoSubIds.split(',').filter((id) => id !== action.payload); + state.remote.ssoSubIds = stateAsArray.join(','); + }, }, extraReducers(builder) { builder.addCase(loadConfigFile.pending, (state) => { @@ -300,12 +309,14 @@ export const { setUpnpState, setWanPortToValue, setWanAccess, + removeSsoUser, } = actions; /** * Actions that should trigger a flash write */ export const configUpdateActionsFlash = isAnyOf( + addSsoUser, updateUserConfig, updateAccessTokens, updateAllowedOrigins, @@ -314,7 +325,8 @@ export const configUpdateActionsFlash = isAnyOf( setWanAccess, setupRemoteAccessThunk.fulfilled, logoutUser.fulfilled, - loginUser.fulfilled + loginUser.fulfilled, + removeSsoUser ); /** diff --git a/api/src/unraid-api/cli/cli.module.ts b/api/src/unraid-api/cli/cli.module.ts index 9f60ea2c7..cd2c901db 100644 --- a/api/src/unraid-api/cli/cli.module.ts +++ b/api/src/unraid-api/cli/cli.module.ts @@ -17,11 +17,15 @@ import { StatusCommand } from '@app/unraid-api/cli/status.command'; import { StopCommand } from '@app/unraid-api/cli/stop.command'; import { SwitchEnvCommand } from '@app/unraid-api/cli/switch-env.command'; import { VersionCommand } from '@app/unraid-api/cli/version.command'; +import { RemoveSSOUserCommand } from '@app/unraid-api/cli/sso/remove-sso-user.command'; +import { RemoveSSOUserQuestionSet } from '@app/unraid-api/cli/sso/remove-sso-user.questions'; @Module({ providers: [ AddSSOUserCommand, AddSSOUserQuestionSet, + RemoveSSOUserCommand, + RemoveSSOUserQuestionSet, LogService, StartCommand, StopCommand, diff --git a/api/src/unraid-api/cli/sso/add-sso-user.questions.ts b/api/src/unraid-api/cli/sso/add-sso-user.questions.ts index 2e3d6c37a..fe2d9b2ab 100644 --- a/api/src/unraid-api/cli/sso/add-sso-user.questions.ts +++ b/api/src/unraid-api/cli/sso/add-sso-user.questions.ts @@ -1,10 +1,6 @@ import { Question, QuestionSet } from 'nest-commander'; import { v4 as uuidv4 } from 'uuid'; - - - - @QuestionSet({ name: 'add-user' }) export class AddSSOUserQuestionSet { static name = 'add-user'; diff --git a/api/src/unraid-api/cli/sso/remove-sso-user.command.ts b/api/src/unraid-api/cli/sso/remove-sso-user.command.ts new file mode 100644 index 000000000..3c249f934 --- /dev/null +++ b/api/src/unraid-api/cli/sso/remove-sso-user.command.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common'; + +import { CommandRunner, InquirerService, Option, OptionChoiceFor, SubCommand } from 'nest-commander'; + +import { store } from '@app/store/index'; +import { loadConfigFile, removeSsoUser } from '@app/store/modules/config'; +import { LogService } from '@app/unraid-api/cli/log.service'; +import { RemoveSSOUserQuestionSet } from '@app/unraid-api/cli/sso/remove-sso-user.questions'; + +interface RemoveSSOUserCommandOptions { + username: string; +} + +@Injectable() +@SubCommand({ + name: 'remove-user', + aliases: ['remove', 'r'], + description: 'Remove a user (or all users) from SSO', +}) +export class RemoveSSOUserCommand extends CommandRunner { + constructor( + private readonly logger: LogService, + private readonly inquirerService: InquirerService + ) { + super(); + } + public async run(_input: string[], options: RemoveSSOUserCommandOptions): Promise { + await store.dispatch(loadConfigFile()); + console.log('options', options); + options = await this.inquirerService.prompt(RemoveSSOUserQuestionSet.name, options); + store.dispatch(removeSsoUser(options.username === 'all' ? null : options.username)); + this.logger.info('User/s removed ' + options.username); + } +} diff --git a/api/src/unraid-api/cli/sso/remove-sso-user.questions.ts b/api/src/unraid-api/cli/sso/remove-sso-user.questions.ts new file mode 100644 index 000000000..2d1c91a64 --- /dev/null +++ b/api/src/unraid-api/cli/sso/remove-sso-user.questions.ts @@ -0,0 +1,28 @@ +import { ChoicesFor, Question, QuestionSet, } from 'nest-commander'; + +import { store } from '@app/store/index'; +import { loadConfigFile } from '@app/store/modules/config'; + + +@QuestionSet({ name: 'remove-user' }) +export class RemoveSSOUserQuestionSet { + static name = 'remove-user'; + + @Question({ + message: `Please select from the following list of users to remove from SSO, or enter all to remove all users from SSO.\n`, + name: 'username', + type: 'list', + }) + parseName(val: string) { + return val; + } + + @ChoicesFor({ name: 'username' }) + async choicesForUsername() { + await store.dispatch(loadConfigFile()); + const users = store.getState().config.remote.ssoSubIds.split(',').filter((user) => user !== ''); + + users.push('all'); + return users; + } +} diff --git a/api/src/unraid-api/cli/sso/sso.command.ts b/api/src/unraid-api/cli/sso/sso.command.ts index 220da3354..d1c19aaa6 100644 --- a/api/src/unraid-api/cli/sso/sso.command.ts +++ b/api/src/unraid-api/cli/sso/sso.command.ts @@ -5,12 +5,13 @@ import { Command, CommandRunner } from 'nest-commander'; import { LogService } from '@app/unraid-api/cli/log.service'; import { ValidateTokenCommand } from '@app/unraid-api/cli/sso/validate-token.command'; import { AddSSOUserCommand } from '@app/unraid-api/cli/sso/add-sso-user.command'; +import { RemoveSSOUserCommand } from '@app/unraid-api/cli/sso/remove-sso-user.command'; @Injectable() @Command({ name: 'sso', description: 'Main Command to Configure / Validate SSO Tokens', - subCommands: [ValidateTokenCommand, AddSSOUserCommand], + subCommands: [ValidateTokenCommand, AddSSOUserCommand, RemoveSSOUserCommand], }) export class SSOCommand extends CommandRunner { constructor(private readonly logger: LogService) {