mirror of
https://github.com/FlowiseAI/Flowise.git
synced 2026-10-12 02:02:17 +00:00
fix: flowise-707 (#6712)
This commit is contained in:
@@ -47,6 +47,7 @@ import { AddCustomMcpServer1766000000000 } from './1766000000000-AddCustomMcpSer
|
||||
import { AddWebhookSecretToChatFlow1776240000003 } from './1776240000003-AddWebhookSecretToChatFlow'
|
||||
import { AddMcpServerConfigToChatFlow1767000000000 } from './1767000000000-AddMcpServerConfigToChatFlow'
|
||||
import { AddScheduleEntities1772000000000 } from './1772000000000-AddScheduleEntities'
|
||||
import { AddSSOIdentityBinding1780000000000 } from '../../../enterprise/database/migrations/mariadb/1780000000000-AddSSOIdentityBinding'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/mariadb/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1725437498242 } from '../../../enterprise/database/migrations/mariadb/1725437498242-AddWorkspace'
|
||||
@@ -120,5 +121,6 @@ export const mariadbMigrations = [
|
||||
AddCustomMcpServer1766000000000,
|
||||
AddWebhookSecretToChatFlow1776240000003,
|
||||
AddMcpServerConfigToChatFlow1767000000000,
|
||||
AddScheduleEntities1772000000000
|
||||
AddScheduleEntities1772000000000,
|
||||
AddSSOIdentityBinding1780000000000
|
||||
]
|
||||
|
||||
@@ -48,6 +48,7 @@ import { AddWebhookSecretToChatFlow1776240000002 } from './1776240000002-AddWebh
|
||||
import { AddCustomMcpServer1766000000000 } from './1766000000000-AddCustomMcpServer'
|
||||
import { AddMcpServerConfigToChatFlow1767000000000 } from './1767000000000-AddMcpServerConfigToChatFlow'
|
||||
import { AddScheduleEntities1772000000000 } from './1772000000000-AddScheduleEntities'
|
||||
import { AddSSOIdentityBinding1780000000000 } from '../../../enterprise/database/migrations/mysql/1780000000000-AddSSOIdentityBinding'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/mysql/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/mysql/1720230151484-AddWorkspace'
|
||||
@@ -122,5 +123,6 @@ export const mysqlMigrations = [
|
||||
AddWebhookSecretToChatFlow1776240000002,
|
||||
AddCustomMcpServer1766000000000,
|
||||
AddMcpServerConfigToChatFlow1767000000000,
|
||||
AddScheduleEntities1772000000000
|
||||
AddScheduleEntities1772000000000,
|
||||
AddSSOIdentityBinding1780000000000
|
||||
]
|
||||
|
||||
@@ -46,6 +46,7 @@ import { AddWebhookSecretToChatFlow1776240000001 } from './1776240000001-AddWebh
|
||||
import { AddCustomMcpServer1766000000000 } from './1766000000000-AddCustomMcpServer'
|
||||
import { AddMcpServerConfigToChatFlow1767000000000 } from './1767000000000-AddMcpServerConfigToChatFlow'
|
||||
import { AddScheduleEntities1772000000000 } from './1772000000000-AddScheduleEntities'
|
||||
import { AddSSOIdentityBinding1780000000000 } from '../../../enterprise/database/migrations/postgres/1780000000000-AddSSOIdentityBinding'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/postgres/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/postgres/1720230151484-AddWorkspace'
|
||||
@@ -118,5 +119,6 @@ export const postgresMigrations = [
|
||||
AddWebhookSecretToChatFlow1776240000001,
|
||||
AddCustomMcpServer1766000000000,
|
||||
AddMcpServerConfigToChatFlow1767000000000,
|
||||
AddScheduleEntities1772000000000
|
||||
AddScheduleEntities1772000000000,
|
||||
AddSSOIdentityBinding1780000000000
|
||||
]
|
||||
|
||||
@@ -44,6 +44,7 @@ import { AddWebhookSecretToChatFlow1776240000000 } from './1776240000000-AddWebh
|
||||
import { AddCustomMcpServer1766000000000 } from './1766000000000-AddCustomMcpServer'
|
||||
import { AddMcpServerConfigToChatFlow1767000000000 } from './1767000000000-AddMcpServerConfigToChatFlow'
|
||||
import { AddScheduleEntities1772000000000 } from './1772000000000-AddScheduleEntities'
|
||||
import { AddSSOIdentityBinding1780000000000 } from '../../../enterprise/database/migrations/sqlite/1780000000000-AddSSOIdentityBinding'
|
||||
|
||||
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/sqlite/1720230151482-AddAuthTables'
|
||||
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/sqlite/1720230151484-AddWorkspace'
|
||||
@@ -114,5 +115,6 @@ export const sqliteMigrations = [
|
||||
AddWebhookSecretToChatFlow1776240000000,
|
||||
AddCustomMcpServer1766000000000,
|
||||
AddMcpServerConfigToChatFlow1767000000000,
|
||||
AddScheduleEntities1772000000000
|
||||
AddScheduleEntities1772000000000,
|
||||
AddSSOIdentityBinding1780000000000
|
||||
]
|
||||
|
||||
@@ -36,6 +36,12 @@ export class User {
|
||||
@Column({ type: 'varchar', length: 20, default: UserStatus.UNVERIFIED })
|
||||
status: string
|
||||
|
||||
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||
ssoProvider?: string | null
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
ssoSubjectId?: string | null
|
||||
|
||||
@CreateDateColumn()
|
||||
createdDate?: Date
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { ensureColumnExists } from './mariaDbCustomFunctions'
|
||||
|
||||
export class AddSSOIdentityBinding1780000000000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await ensureColumnExists(queryRunner, 'user', 'ssoProvider', 'varchar(100)')
|
||||
await ensureColumnExists(queryRunner, 'user', 'ssoSubjectId', 'varchar(255)')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`user\` DROP COLUMN \`ssoProvider\`;`)
|
||||
await queryRunner.query(`ALTER TABLE \`user\` DROP COLUMN \`ssoSubjectId\`;`)
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { ensureColumnExists } from './mysqlCustomFunctions'
|
||||
|
||||
export class AddSSOIdentityBinding1780000000000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await ensureColumnExists(queryRunner, 'user', 'ssoProvider', 'varchar(100)')
|
||||
await ensureColumnExists(queryRunner, 'user', 'ssoSubjectId', 'varchar(255)')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`user\` DROP COLUMN \`ssoProvider\`;`)
|
||||
await queryRunner.query(`ALTER TABLE \`user\` DROP COLUMN \`ssoSubjectId\`;`)
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSSOIdentityBinding1780000000000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "ssoProvider" varchar;`)
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "ssoSubjectId" varchar;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "ssoProvider";`)
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "ssoSubjectId";`)
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
import { ensureColumnExists } from './sqlliteCustomFunctions'
|
||||
|
||||
export class AddSSOIdentityBinding1780000000000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await ensureColumnExists(queryRunner, 'user', 'ssoProvider', 'varchar')
|
||||
await ensureColumnExists(queryRunner, 'user', 'ssoSubjectId', 'varchar')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "ssoProvider";`)
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "ssoSubjectId";`)
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,9 @@ export const enum UserErrorMessage {
|
||||
INCORRECT_USER_EMAIL_OR_CREDENTIALS = 'Incorrect Email or Password',
|
||||
PASSWORDS_DO_NOT_MATCH = 'Passwords do not match',
|
||||
EMAIL_CHANGE_USE_CONFIRM_LINK = 'Use the confirm email change link from your email to complete this action.',
|
||||
USER_INVITED_PENDING_ACTIVATION = 'You have a pending invitation. Please check your email and complete the invitation to activate your account before signing in.'
|
||||
USER_INVITED_PENDING_ACTIVATION = 'You have a pending invitation. Please check your email and complete the invitation to activate your account before signing in.',
|
||||
SSO_IDENTITY_MISMATCH = 'This account is linked to a different sign-in method. Please use your original sign-in method, or contact your administrator to update it.',
|
||||
SSO_SUBJECT_ID_MISSING = 'Your identity provider did not return a valid user identifier. Please contact your administrator.'
|
||||
}
|
||||
export class UserService {
|
||||
private telemetry: Telemetry
|
||||
@@ -66,6 +68,10 @@ export class UserService {
|
||||
return await queryRunner.manager.findOneBy(User, { tempToken: token })
|
||||
}
|
||||
|
||||
public async bindSSOIdentity(userId: string, ssoProvider: string, ssoSubjectId: string, queryRunner: QueryRunner) {
|
||||
return await queryRunner.manager.update(User, { id: userId }, { ssoProvider, ssoSubjectId })
|
||||
}
|
||||
|
||||
public validateUserStatus(status: string | undefined) {
|
||||
if (status && !Object.values(UserStatus).includes(status as UserStatus))
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, UserErrorMessage.INVALID_USER_STATUS)
|
||||
|
||||
@@ -5,26 +5,59 @@ import { UserStatus } from '../database/entities/user.entity'
|
||||
import { UserErrorMessage } from '../services/user.service'
|
||||
|
||||
const mockReadUserByEmail = jest.fn() as jest.MockedFunction<(_email: string, _queryRunner: QueryRunner) => Promise<any>>
|
||||
const mockBindSSOIdentity = jest.fn() as jest.MockedFunction<
|
||||
(_userId: string, _ssoProvider: string, _ssoSubjectId: string, _queryRunner: QueryRunner) => Promise<any>
|
||||
>
|
||||
const mockCreateQueryRunner = jest.fn() as jest.MockedFunction<() => any>
|
||||
const mockGetPlatformType = jest.fn() as jest.MockedFunction<() => Platform>
|
||||
const mockGetFeaturesByPlan = jest.fn() as jest.MockedFunction<(_subscriptionId: string) => Promise<any>>
|
||||
const mockGetProductIdFromSubscription = jest.fn() as jest.MockedFunction<(_subscriptionId: string) => Promise<any>>
|
||||
const mockReadWorkspaceUserByLastLogin = jest.fn() as jest.MockedFunction<(_userId: string, _queryRunner: QueryRunner) => Promise<any>>
|
||||
const mockReadWorkspaceUserByUserId = jest.fn() as jest.MockedFunction<(_userId: string, _queryRunner: QueryRunner) => Promise<any>>
|
||||
const mockReadGeneralRoleByName = jest.fn() as jest.MockedFunction<(_name: string, _queryRunner: QueryRunner) => Promise<any>>
|
||||
const mockReadRoleById = jest.fn() as jest.MockedFunction<(_id: string, _queryRunner: QueryRunner) => Promise<any>>
|
||||
const mockReadOrganizationById = jest.fn() as jest.MockedFunction<(_id: string, _queryRunner: QueryRunner) => Promise<any>>
|
||||
const mockRecordLoginActivity = jest.fn() as jest.MockedFunction<(..._args: any[]) => Promise<any>>
|
||||
|
||||
jest.mock('../../utils/getRunningExpressApp', () => ({
|
||||
getRunningExpressApp: jest.fn().mockImplementation(() => ({
|
||||
AppDataSource: { createQueryRunner: mockCreateQueryRunner },
|
||||
identityManager: { getPlatformType: mockGetPlatformType }
|
||||
identityManager: {
|
||||
getPlatformType: mockGetPlatformType,
|
||||
getFeaturesByPlan: mockGetFeaturesByPlan,
|
||||
getProductIdFromSubscription: mockGetProductIdFromSubscription
|
||||
}
|
||||
}))
|
||||
}))
|
||||
|
||||
jest.mock('../services/user.service', () => ({
|
||||
UserService: jest.fn().mockImplementation(() => ({ readUserByEmail: mockReadUserByEmail }))
|
||||
UserService: jest.fn().mockImplementation(() => ({
|
||||
readUserByEmail: mockReadUserByEmail,
|
||||
bindSSOIdentity: mockBindSSOIdentity
|
||||
}))
|
||||
}))
|
||||
|
||||
jest.mock('../services/organization.service', () => ({
|
||||
OrganizationService: jest.fn().mockImplementation(() => ({}))
|
||||
OrganizationService: jest.fn().mockImplementation(() => ({ readOrganizationById: mockReadOrganizationById }))
|
||||
}))
|
||||
|
||||
jest.mock('../services/workspace-user.service', () => ({
|
||||
WorkspaceUserService: jest.fn().mockImplementation(() => ({}))
|
||||
WorkspaceUserService: jest.fn().mockImplementation(() => ({
|
||||
readWorkspaceUserByLastLogin: mockReadWorkspaceUserByLastLogin,
|
||||
readWorkspaceUserByUserId: mockReadWorkspaceUserByUserId
|
||||
}))
|
||||
}))
|
||||
|
||||
jest.mock('../services/role.service', () => ({
|
||||
RoleService: jest.fn().mockImplementation(() => ({
|
||||
readGeneralRoleByName: mockReadGeneralRoleByName,
|
||||
readRoleById: mockReadRoleById
|
||||
}))
|
||||
}))
|
||||
|
||||
jest.mock('../services/audit', () => ({
|
||||
__esModule: true,
|
||||
default: { recordLoginActivity: mockRecordLoginActivity }
|
||||
}))
|
||||
|
||||
import SSOBase from './SSOBase'
|
||||
@@ -43,15 +76,32 @@ function makeQueryRunner() {
|
||||
return { connect: jest.fn(), release: jest.fn(), isReleased: false }
|
||||
}
|
||||
|
||||
const WORKSPACE_USER = {
|
||||
userId: 'user-1',
|
||||
roleId: 'role-1',
|
||||
workspaceId: 'workspace-1',
|
||||
workspace: { id: 'workspace-1', name: 'Default Workspace', organizationId: 'org-1' }
|
||||
}
|
||||
|
||||
function mockHappyPathDependencies() {
|
||||
mockReadWorkspaceUserByLastLogin.mockResolvedValue(WORKSPACE_USER)
|
||||
mockReadWorkspaceUserByUserId.mockResolvedValue([{ ...WORKSPACE_USER, role: { name: 'Owner' } }])
|
||||
mockReadGeneralRoleByName.mockResolvedValue({ id: 'role-1' })
|
||||
mockReadRoleById.mockResolvedValue({ id: 'role-1', permissions: '[]' })
|
||||
mockReadOrganizationById.mockResolvedValue({ id: 'org-1', subscriptionId: 'sub-1', customerId: 'cust-1' })
|
||||
mockGetFeaturesByPlan.mockResolvedValue({})
|
||||
mockGetProductIdFromSubscription.mockResolvedValue('product-1')
|
||||
}
|
||||
|
||||
describe('SSOBase.verifyAndLogin', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
mockGetPlatformType.mockReturnValue(Platform.ENTERPRISE)
|
||||
})
|
||||
|
||||
it('rejects an INVITED user instead of auto-activating them (invite-token bypass fix)', async () => {
|
||||
const queryRunner = makeQueryRunner()
|
||||
mockCreateQueryRunner.mockReturnValue(queryRunner)
|
||||
mockGetPlatformType.mockReturnValue(Platform.ENTERPRISE)
|
||||
mockReadUserByEmail.mockResolvedValue({
|
||||
id: 'user-1',
|
||||
email: '[email protected]',
|
||||
@@ -79,4 +129,88 @@ describe('SSOBase.verifyAndLogin', () => {
|
||||
)
|
||||
expect(queryRunner.release).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('binds an ACTIVE user to their SSO identity on first login (trust-on-first-use)', async () => {
|
||||
const queryRunner = makeQueryRunner()
|
||||
mockCreateQueryRunner.mockReturnValue(queryRunner)
|
||||
mockReadUserByEmail.mockResolvedValue({
|
||||
id: 'user-1',
|
||||
email: '[email protected]',
|
||||
status: UserStatus.ACTIVE,
|
||||
ssoProvider: null,
|
||||
ssoSubjectId: null
|
||||
})
|
||||
mockHappyPathDependencies()
|
||||
|
||||
const sso = new TestSSO({} as any)
|
||||
const done = jest.fn()
|
||||
|
||||
await sso.verifyAndLogin({} as any, '[email protected]', done, { id: 'sub-123', displayName: 'User' } as any, 'at', 'rt')
|
||||
|
||||
expect(mockBindSSOIdentity).toHaveBeenCalledWith('user-1', 'Test SSO', 'sub-123', queryRunner)
|
||||
expect(done).toHaveBeenCalledWith(null, expect.objectContaining({ id: 'user-1' }), { message: 'Logged in Successfully' })
|
||||
})
|
||||
|
||||
it('allows a returning SSO login whose identity matches the stored binding', async () => {
|
||||
const queryRunner = makeQueryRunner()
|
||||
mockCreateQueryRunner.mockReturnValue(queryRunner)
|
||||
mockReadUserByEmail.mockResolvedValue({
|
||||
id: 'user-1',
|
||||
email: '[email protected]',
|
||||
status: UserStatus.ACTIVE,
|
||||
ssoProvider: 'Test SSO',
|
||||
ssoSubjectId: 'sub-123'
|
||||
})
|
||||
mockHappyPathDependencies()
|
||||
|
||||
const sso = new TestSSO({} as any)
|
||||
const done = jest.fn()
|
||||
|
||||
await sso.verifyAndLogin({} as any, '[email protected]', done, { id: 'sub-123', displayName: 'User' } as any, 'at', 'rt')
|
||||
|
||||
expect(mockBindSSOIdentity).not.toHaveBeenCalled()
|
||||
expect(done).toHaveBeenCalledWith(null, expect.objectContaining({ id: 'user-1' }), { message: 'Logged in Successfully' })
|
||||
})
|
||||
|
||||
it('rejects a login whose SSO identity does not match the stored binding (cross-provider account takeover attempt)', async () => {
|
||||
const queryRunner = makeQueryRunner()
|
||||
mockCreateQueryRunner.mockReturnValue(queryRunner)
|
||||
mockReadUserByEmail.mockResolvedValue({
|
||||
id: 'user-1',
|
||||
email: '[email protected]',
|
||||
status: UserStatus.ACTIVE,
|
||||
ssoProvider: 'Google SSO',
|
||||
ssoSubjectId: 'google-sub-1'
|
||||
})
|
||||
|
||||
const sso = new TestSSO({} as any)
|
||||
const done = jest.fn()
|
||||
|
||||
await sso.verifyAndLogin({} as any, '[email protected]', done, { id: 'auth0-sub-999', displayName: 'Attacker' } as any, 'at', 'rt')
|
||||
|
||||
expect(mockBindSSOIdentity).not.toHaveBeenCalled()
|
||||
expect(mockRecordLoginActivity).toHaveBeenCalled()
|
||||
expect(done).toHaveBeenCalledWith({ name: 'SSO_LOGIN_FAILED', message: UserErrorMessage.SSO_IDENTITY_MISMATCH }, undefined)
|
||||
expect(queryRunner.release).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('rejects a login when the provider does not supply a stable subject id', async () => {
|
||||
const queryRunner = makeQueryRunner()
|
||||
mockCreateQueryRunner.mockReturnValue(queryRunner)
|
||||
mockReadUserByEmail.mockResolvedValue({
|
||||
id: 'user-1',
|
||||
email: '[email protected]',
|
||||
status: UserStatus.ACTIVE,
|
||||
ssoProvider: null,
|
||||
ssoSubjectId: null
|
||||
})
|
||||
|
||||
const sso = new TestSSO({} as any)
|
||||
const done = jest.fn()
|
||||
|
||||
await sso.verifyAndLogin({} as any, '[email protected]', done, { displayName: 'No Subject' } as any, 'at', 'rt')
|
||||
|
||||
expect(mockBindSSOIdentity).not.toHaveBeenCalled()
|
||||
expect(done).toHaveBeenCalledWith({ name: 'SSO_LOGIN_FAILED', message: UserErrorMessage.SSO_SUBJECT_ID_MISSING }, undefined)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// SSOBase.ts
|
||||
import express from 'express'
|
||||
import passport from 'passport'
|
||||
import { IAssignedWorkspace, LoggedInUser } from '../Interface.Enterprise'
|
||||
import { IAssignedWorkspace, LoggedInUser, LoginActivityCode } from '../Interface.Enterprise'
|
||||
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||
import auditService from '../services/audit'
|
||||
import { UserErrorMessage, UserService } from '../services/user.service'
|
||||
import { WorkspaceUserService } from '../services/workspace-user.service'
|
||||
import { WorkspaceUser } from '../database/entities/workspace-user.entity'
|
||||
@@ -71,6 +72,26 @@ abstract class SSOBase {
|
||||
// identity provider's email assertion, which is not equivalent proof.
|
||||
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, UserErrorMessage.USER_INVITED_PENDING_ACTIVATION)
|
||||
}
|
||||
|
||||
const providerSubjectId = profile?.id
|
||||
if (!providerSubjectId) {
|
||||
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, UserErrorMessage.SSO_SUBJECT_ID_MISSING)
|
||||
}
|
||||
|
||||
if (user.ssoProvider && user.ssoSubjectId) {
|
||||
if (user.ssoProvider !== ssoProviderName || user.ssoSubjectId !== providerSubjectId) {
|
||||
await auditService.recordLoginActivity(
|
||||
user.email,
|
||||
LoginActivityCode.INVALID_LOGIN_MODE,
|
||||
UserErrorMessage.SSO_IDENTITY_MISMATCH,
|
||||
ssoProviderName
|
||||
)
|
||||
throw new InternalFlowiseError(StatusCodes.FORBIDDEN, UserErrorMessage.SSO_IDENTITY_MISMATCH)
|
||||
}
|
||||
} else {
|
||||
await userService.bindSSOIdentity(user.id, ssoProviderName, providerSubjectId, queryRunner)
|
||||
}
|
||||
|
||||
let wsUserOrUsers = await workspaceUserService.readWorkspaceUserByLastLogin(user?.id, queryRunner)
|
||||
wu = Array.isArray(wsUserOrUsers) && wsUserOrUsers.length > 0 ? wsUserOrUsers[0] : (wsUserOrUsers as WorkspaceUser)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const Evaluators = Loadable(lazy(() => import('@/views/evaluators')))
|
||||
const Account = Loadable(lazy(() => import('@/views/account')))
|
||||
|
||||
// files routing
|
||||
const Files = Loadable(lazy(() => import('@/views/files')))
|
||||
// const Files = Loadable(lazy(() => import('@/views/files')))
|
||||
|
||||
// logs routing
|
||||
const Logs = Loadable(lazy(() => import('@/views/serverlogs')))
|
||||
|
||||
Reference in New Issue
Block a user