mirror of https://github.com/buster-so/buster.git
remove delted at clause on user
This commit is contained in:
parent
2bc11900b9
commit
cbd8b21203
|
@ -20,7 +20,7 @@ describe('slack-authentication', () => {
|
||||||
const mockIntegration = {
|
const mockIntegration = {
|
||||||
id: 'int-123',
|
id: 'int-123',
|
||||||
organizationId: 'org-123',
|
organizationId: 'org-123',
|
||||||
userId: 'user-123',
|
userId: 'installer-123',
|
||||||
tokenVaultKey: 'token-key',
|
tokenVaultKey: 'token-key',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ describe('slack-authentication', () => {
|
||||||
mockIntegration as any
|
mockIntegration as any
|
||||||
);
|
);
|
||||||
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
||||||
vi.mocked(SlackHelpers.getUserById).mockResolvedValue(mockUser as any);
|
vi.mocked(SlackHelpers.getUserByEmail).mockResolvedValue(mockUser as any);
|
||||||
|
|
||||||
const mockSlackUserService = {
|
const mockSlackUserService = {
|
||||||
isBot: vi.fn().mockResolvedValue(false),
|
isBot: vi.fn().mockResolvedValue(false),
|
||||||
|
@ -76,6 +76,11 @@ describe('slack-authentication', () => {
|
||||||
expect(mockSlackUserService.isBot).toHaveBeenCalledWith('slack-token', 'U123456');
|
expect(mockSlackUserService.isBot).toHaveBeenCalledWith('slack-token', 'U123456');
|
||||||
expect(mockSlackUserService.isDeleted).toHaveBeenCalledWith('slack-token', 'U123456');
|
expect(mockSlackUserService.isDeleted).toHaveBeenCalledWith('slack-token', 'U123456');
|
||||||
expect(mockSlackUserService.getUserInfo).toHaveBeenCalledWith('slack-token', 'U123456');
|
expect(mockSlackUserService.getUserInfo).toHaveBeenCalledWith('slack-token', 'U123456');
|
||||||
|
expect(vi.mocked(SlackHelpers.getUserByEmail)).toHaveBeenCalledWith('john@example.com');
|
||||||
|
expect(vi.mocked(accessControls.checkUserInOrganization)).toHaveBeenCalledWith(
|
||||||
|
'user-123',
|
||||||
|
'org-123'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return unauthorized for bot users', async () => {
|
it('should return unauthorized for bot users', async () => {
|
||||||
|
@ -152,14 +157,21 @@ describe('slack-authentication', () => {
|
||||||
const mockIntegration = {
|
const mockIntegration = {
|
||||||
id: 'int-123',
|
id: 'int-123',
|
||||||
organizationId: 'org-123',
|
organizationId: 'org-123',
|
||||||
userId: 'user-123',
|
userId: 'installer-123',
|
||||||
tokenVaultKey: 'token-key',
|
tokenVaultKey: 'token-key',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockUser = {
|
||||||
|
id: 'user-123',
|
||||||
|
email: 'john@example.com',
|
||||||
|
name: 'John Doe',
|
||||||
|
};
|
||||||
|
|
||||||
vi.mocked(SlackHelpers.getActiveIntegrationByTeamId).mockResolvedValue(
|
vi.mocked(SlackHelpers.getActiveIntegrationByTeamId).mockResolvedValue(
|
||||||
mockIntegration as any
|
mockIntegration as any
|
||||||
);
|
);
|
||||||
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
||||||
|
vi.mocked(SlackHelpers.getUserByEmail).mockResolvedValue(mockUser as any);
|
||||||
|
|
||||||
const mockSlackUserService = {
|
const mockSlackUserService = {
|
||||||
isBot: vi.fn().mockResolvedValue(false),
|
isBot: vi.fn().mockResolvedValue(false),
|
||||||
|
@ -215,6 +227,7 @@ describe('slack-authentication', () => {
|
||||||
mockIntegration as any
|
mockIntegration as any
|
||||||
);
|
);
|
||||||
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
||||||
|
vi.mocked(SlackHelpers.getUserByEmail).mockResolvedValue(null); // No existing user
|
||||||
|
|
||||||
const mockSlackUserService = {
|
const mockSlackUserService = {
|
||||||
isBot: vi.fn().mockResolvedValue(false),
|
isBot: vi.fn().mockResolvedValue(false),
|
||||||
|
@ -230,7 +243,6 @@ describe('slack-authentication', () => {
|
||||||
};
|
};
|
||||||
vi.mocked(SlackUserService).mockImplementation(() => mockSlackUserService as any);
|
vi.mocked(SlackUserService).mockImplementation(() => mockSlackUserService as any);
|
||||||
|
|
||||||
vi.mocked(accessControls.checkUserInOrganization).mockResolvedValue(null);
|
|
||||||
vi.mocked(accessControls.getOrganizationWithDefaults).mockResolvedValue(mockOrg as any);
|
vi.mocked(accessControls.getOrganizationWithDefaults).mockResolvedValue(mockOrg as any);
|
||||||
vi.mocked(accessControls.checkEmailDomainForOrganization).mockResolvedValue(true);
|
vi.mocked(accessControls.checkEmailDomainForOrganization).mockResolvedValue(true);
|
||||||
vi.mocked(accessControls.createUserInOrganization).mockResolvedValue({
|
vi.mocked(accessControls.createUserInOrganization).mockResolvedValue({
|
||||||
|
@ -259,6 +271,69 @@ describe('slack-authentication', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should auto-provision existing user when they are not in org but domain matches', async () => {
|
||||||
|
const mockIntegration = {
|
||||||
|
id: 'int-123',
|
||||||
|
organizationId: 'org-123',
|
||||||
|
userId: 'installer-123',
|
||||||
|
tokenVaultKey: 'token-key',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockOrg = {
|
||||||
|
id: 'org-123',
|
||||||
|
name: 'Example Org',
|
||||||
|
defaultRole: 'restricted_querier',
|
||||||
|
domains: ['example.com'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockExistingUser = {
|
||||||
|
id: 'existing-user-123',
|
||||||
|
email: 'existing@example.com',
|
||||||
|
name: 'Existing User',
|
||||||
|
};
|
||||||
|
|
||||||
|
vi.mocked(SlackHelpers.getActiveIntegrationByTeamId).mockResolvedValue(
|
||||||
|
mockIntegration as any
|
||||||
|
);
|
||||||
|
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
||||||
|
vi.mocked(SlackHelpers.getUserByEmail).mockResolvedValue(mockExistingUser as any);
|
||||||
|
|
||||||
|
const mockSlackUserService = {
|
||||||
|
isBot: vi.fn().mockResolvedValue(false),
|
||||||
|
isDeleted: vi.fn().mockResolvedValue(false),
|
||||||
|
getUserInfo: vi.fn().mockResolvedValue({
|
||||||
|
id: 'U123456',
|
||||||
|
real_name: 'Existing User',
|
||||||
|
name: 'existing',
|
||||||
|
profile: {
|
||||||
|
email: 'existing@example.com',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
vi.mocked(SlackUserService).mockImplementation(() => mockSlackUserService as any);
|
||||||
|
|
||||||
|
vi.mocked(accessControls.checkUserInOrganization).mockResolvedValue(null); // Not in org
|
||||||
|
vi.mocked(accessControls.getOrganizationWithDefaults).mockResolvedValue(mockOrg as any);
|
||||||
|
vi.mocked(accessControls.checkEmailDomainForOrganization).mockResolvedValue(true);
|
||||||
|
vi.mocked(accessControls.createUserInOrganization).mockResolvedValue({
|
||||||
|
user: mockExistingUser as any,
|
||||||
|
membership: {
|
||||||
|
userId: 'existing-user-123',
|
||||||
|
organizationId: 'org-123',
|
||||||
|
role: 'restricted_querier',
|
||||||
|
status: 'active',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await authenticateSlackUser('U123456', 'T123456');
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'auto_provisioned',
|
||||||
|
user: mockExistingUser,
|
||||||
|
organization: mockOrg,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return unauthorized when domain does not match', async () => {
|
it('should return unauthorized when domain does not match', async () => {
|
||||||
const mockIntegration = {
|
const mockIntegration = {
|
||||||
id: 'int-123',
|
id: 'int-123',
|
||||||
|
@ -278,6 +353,7 @@ describe('slack-authentication', () => {
|
||||||
mockIntegration as any
|
mockIntegration as any
|
||||||
);
|
);
|
||||||
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
vi.mocked(SlackHelpers.getAccessToken).mockResolvedValue('slack-token');
|
||||||
|
vi.mocked(SlackHelpers.getUserByEmail).mockResolvedValue(null); // No existing user
|
||||||
|
|
||||||
const mockSlackUserService = {
|
const mockSlackUserService = {
|
||||||
isBot: vi.fn().mockResolvedValue(false),
|
isBot: vi.fn().mockResolvedValue(false),
|
||||||
|
@ -293,7 +369,6 @@ describe('slack-authentication', () => {
|
||||||
};
|
};
|
||||||
vi.mocked(SlackUserService).mockImplementation(() => mockSlackUserService as any);
|
vi.mocked(SlackUserService).mockImplementation(() => mockSlackUserService as any);
|
||||||
|
|
||||||
vi.mocked(accessControls.checkUserInOrganization).mockResolvedValue(null);
|
|
||||||
vi.mocked(accessControls.getOrganizationWithDefaults).mockResolvedValue(mockOrg as any);
|
vi.mocked(accessControls.getOrganizationWithDefaults).mockResolvedValue(mockOrg as any);
|
||||||
vi.mocked(accessControls.checkEmailDomainForOrganization).mockResolvedValue(false);
|
vi.mocked(accessControls.checkEmailDomainForOrganization).mockResolvedValue(false);
|
||||||
|
|
||||||
|
|
|
@ -98,39 +98,42 @@ export async function authenticateSlackUser(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user exists in the organization
|
// Check if a Buster user exists with this email
|
||||||
const userOrgInfo = await checkUserInOrganization(
|
const existingUser = await SlackHelpers.getUserByEmail(userEmail);
|
||||||
integration.userId,
|
|
||||||
integration.organizationId
|
if (existingUser) {
|
||||||
);
|
// User exists - check if they belong to this organization
|
||||||
|
const userOrgInfo = await checkUserInOrganization(
|
||||||
|
existingUser.id,
|
||||||
|
integration.organizationId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (userOrgInfo) {
|
||||||
|
// User exists in organization - check their status
|
||||||
|
if (userOrgInfo.status !== 'active') {
|
||||||
|
return {
|
||||||
|
type: 'unauthorized',
|
||||||
|
reason: `User account is ${userOrgInfo.status}. Please contact your administrator.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get organization data
|
||||||
|
const organization = await getOrganizationWithDefaults(integration.organizationId);
|
||||||
|
|
||||||
|
if (!organization) {
|
||||||
|
return {
|
||||||
|
type: 'unauthorized',
|
||||||
|
reason: 'Failed to load organization data',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (userOrgInfo) {
|
|
||||||
// User exists - check their status
|
|
||||||
if (userOrgInfo.status !== 'active') {
|
|
||||||
return {
|
return {
|
||||||
type: 'unauthorized',
|
type: 'authorized',
|
||||||
reason: `User account is ${userOrgInfo.status}. Please contact your administrator.`,
|
user: existingUser,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// User exists but not in this organization - fall through to domain check
|
||||||
// Get full user and organization data concurrently
|
|
||||||
const [user, organization] = await Promise.all([
|
|
||||||
SlackHelpers.getUserById(integration.userId),
|
|
||||||
getOrganizationWithDefaults(integration.organizationId),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!user || !organization) {
|
|
||||||
return {
|
|
||||||
type: 'unauthorized',
|
|
||||||
reason: 'Failed to load user or organization data',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'authorized',
|
|
||||||
user,
|
|
||||||
organization,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// User doesn't exist - check if we can auto-provision
|
// User doesn't exist - check if we can auto-provision
|
||||||
|
|
|
@ -460,6 +460,26 @@ export async function getUserById(userId: string): Promise<User | null> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user by email address
|
||||||
|
*/
|
||||||
|
export async function getUserByEmail(email: string): Promise<User | null> {
|
||||||
|
try {
|
||||||
|
const [user] = await db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.where(and(eq(users.email, email)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return user || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get user by email:', error);
|
||||||
|
throw new Error(
|
||||||
|
`Failed to get user by email: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Export as namespace for easier import
|
// Export as namespace for easier import
|
||||||
export const SlackHelpers = {
|
export const SlackHelpers = {
|
||||||
getActiveIntegration,
|
getActiveIntegration,
|
||||||
|
@ -477,4 +497,5 @@ export const SlackHelpers = {
|
||||||
getActiveIntegrationByTeamId,
|
getActiveIntegrationByTeamId,
|
||||||
getAccessToken,
|
getAccessToken,
|
||||||
getUserById,
|
getUserById,
|
||||||
|
getUserByEmail,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue