[BREAK] Moved advanced oAuth features to EE (#23201)
Co-authored-by: Pierre Lehnen <pierre.lehnen@rocket.chat>pull/23218/head
parent
a41ac6498a
commit
5e458320b7
@ -1,73 +0,0 @@ |
||||
import { addUserRoles, removeUserFromRoles } from '../../authorization'; |
||||
import { Roles, Rooms } from '../../models'; |
||||
import { addUserToRoom, createRoom } from '../../lib/server/functions'; |
||||
import { Logger } from '../../logger'; |
||||
|
||||
export const logger = new Logger('OAuth'); |
||||
|
||||
// Returns list of roles from SSO identity
|
||||
export function mapRolesFromSSO(identity, roleClaimName) { |
||||
let roles = []; |
||||
|
||||
if (identity && roleClaimName) { |
||||
// Adding roles
|
||||
if (identity[roleClaimName] && Array.isArray(identity[roleClaimName])) { |
||||
roles = identity[roleClaimName].filter((val) => val !== 'offline_access' && val !== 'uma_authorization' && Roles.findOneByIdOrName(val)); |
||||
} |
||||
} |
||||
|
||||
return roles; |
||||
} |
||||
|
||||
// Updates the user with roles from SSO identity
|
||||
export function updateRolesFromSSO(user, identity, roleClaimName) { |
||||
if (user && identity && roleClaimName) { |
||||
const rolesFromSSO = mapRolesFromSSO(identity, roleClaimName); |
||||
|
||||
if (!Array.isArray(user.roles)) { |
||||
user.roles = []; |
||||
} |
||||
|
||||
const toRemove = user.roles.filter((val) => !rolesFromSSO.includes(val)); |
||||
|
||||
// loop through roles that user has that sso doesnt have and remove
|
||||
toRemove.forEach(function(role) { |
||||
removeUserFromRoles(user._id, role); |
||||
}); |
||||
|
||||
const toAdd = rolesFromSSO.filter((val) => !user.roles.includes(val)); |
||||
|
||||
// loop through roles sso has that user doesnt and add
|
||||
toAdd.forEach(function(role) { |
||||
addUserRoles(user._id, role); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
export function mapSSOGroupsToChannels(user, identity, groupClaimName, channelsMap, channelsAdmin) { |
||||
if (user && identity && groupClaimName) { |
||||
const groupsFromSSO = identity[groupClaimName] || []; |
||||
|
||||
for (const ssoGroup in channelsMap) { |
||||
if (typeof ssoGroup === 'string') { |
||||
let channels = channelsMap[ssoGroup]; |
||||
if (!Array.isArray(channels)) { |
||||
channels = [channels]; |
||||
} |
||||
for (const channel of channels) { |
||||
let room = Rooms.findOneByNonValidatedName(channel); |
||||
if (!room) { |
||||
room = createRoom('c', channel, channelsAdmin, [], false); |
||||
if (!room || !room.rid) { |
||||
logger.error(`could not create channel ${ channel }`); |
||||
return; |
||||
} |
||||
} |
||||
if (Array.isArray(groupsFromSSO) && groupsFromSSO.includes(ssoGroup)) { |
||||
addUserToRoom(room._id, user); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,2 +1,3 @@ |
||||
import './ldap'; |
||||
import './oauth'; |
||||
import './saml'; |
||||
|
@ -0,0 +1,81 @@ |
||||
import { capitalize } from '@rocket.chat/string-helpers'; |
||||
|
||||
import { OAuthEEManager } from '../lib/oauth/Manager'; |
||||
import { onLicense } from '../../app/license/server'; |
||||
import { callbacks } from '../../../app/callbacks/server'; |
||||
import { settings } from '../../../app/settings/server'; |
||||
import { Logger } from '../../../app/logger/server'; |
||||
|
||||
interface IOAuthUserService { |
||||
serviceName: string; |
||||
serviceData: Record<string, any>; |
||||
user: Record<string, any>; |
||||
} |
||||
|
||||
interface IOAuthUserIdentity { |
||||
serviceName: string; |
||||
identity: Record<string, any>; |
||||
user: Record<string, any>; |
||||
} |
||||
|
||||
interface IOAuthSettings { |
||||
mapChannels: string; |
||||
mergeRoles: string; |
||||
rolesClaim: string; |
||||
groupsClaim: string; |
||||
channelsAdmin: string; |
||||
channelsMap: string; |
||||
} |
||||
|
||||
const logger = new Logger('EECustomOAuth'); |
||||
|
||||
function getOAuthSettings(serviceName: string): IOAuthSettings { |
||||
return { |
||||
mapChannels: settings.get(`Accounts_OAuth_Custom-${ serviceName }-map_channels`) as string, |
||||
mergeRoles: settings.get(`Accounts_OAuth_Custom-${ serviceName }-merge_roles`) as string, |
||||
rolesClaim: settings.get(`Accounts_OAuth_Custom-${ serviceName }-roles_claim`) as string, |
||||
groupsClaim: settings.get(`Accounts_OAuth_Custom-${ serviceName }-groups_claim`) as string, |
||||
channelsAdmin: settings.get(`Accounts_OAuth_Custom-${ serviceName }-channels_admin`) as string, |
||||
channelsMap: settings.get(`Accounts_OAuth_Custom-${ serviceName }-channels_map`) as string, |
||||
}; |
||||
} |
||||
|
||||
function getChannelsMap(channelsMap: string): Record<string, any> | undefined { |
||||
channelsMap = (channelsMap || '{}').trim(); |
||||
|
||||
try { |
||||
return JSON.parse(channelsMap); |
||||
} catch (err) { |
||||
logger.error(`Unexpected error : ${ err }`); |
||||
} |
||||
} |
||||
|
||||
onLicense('oauth-enterprise', () => { |
||||
callbacks.add('afterProcessOAuthUser', (auth: IOAuthUserService) => { |
||||
auth.serviceName = capitalize(auth.serviceName); |
||||
const settings = getOAuthSettings(auth.serviceName); |
||||
|
||||
if (settings.mapChannels) { |
||||
const channelsMap = getChannelsMap(settings.channelsMap); |
||||
OAuthEEManager.mapSSOGroupsToChannels(auth.user, auth.serviceData, settings.groupsClaim, channelsMap, settings.channelsAdmin); |
||||
} |
||||
|
||||
if (settings.mergeRoles) { |
||||
OAuthEEManager.updateRolesFromSSO(auth.user, auth.serviceData, settings.rolesClaim); |
||||
} |
||||
}); |
||||
|
||||
callbacks.add('afterValidateNewOAuthUser', (auth: IOAuthUserIdentity) => { |
||||
auth.serviceName = capitalize(auth.serviceName); |
||||
const settings = getOAuthSettings(auth.serviceName); |
||||
|
||||
if (settings.mapChannels) { |
||||
const channelsMap = getChannelsMap(settings.channelsMap); |
||||
OAuthEEManager.mapSSOGroupsToChannels(auth.user, auth.identity, settings.groupsClaim, channelsMap, settings.channelsAdmin); |
||||
} |
||||
|
||||
if (settings.mergeRoles) { |
||||
auth.user.roles = OAuthEEManager.mapRolesFromSSO(auth.identity, settings.rolesClaim); |
||||
} |
||||
}); |
||||
}); |
@ -0,0 +1,73 @@ |
||||
import { addUserRoles, removeUserFromRoles } from '../../../../app/authorization/server'; |
||||
import { Roles, Rooms } from '../../../../app/models/server'; |
||||
import { addUserToRoom, createRoom } from '../../../../app/lib/server/functions'; |
||||
import { Logger } from '../../../../app/logger/server'; |
||||
|
||||
export const logger = new Logger('OAuth'); |
||||
|
||||
export class OAuthEEManager { |
||||
static mapSSOGroupsToChannels(user: Record<string, any>, identity: Record<string, any>, groupClaimName: string, channelsMap: Record<string, any> | undefined, channelsAdmin: string): void { |
||||
if (user && identity && groupClaimName) { |
||||
const groupsFromSSO = identity[groupClaimName] || []; |
||||
|
||||
for (const ssoGroup in channelsMap) { |
||||
if (typeof ssoGroup === 'string') { |
||||
let channels = channelsMap[ssoGroup]; |
||||
if (!Array.isArray(channels)) { |
||||
channels = [channels]; |
||||
} |
||||
for (const channel of channels) { |
||||
let room = Rooms.findOneByNonValidatedName(channel); |
||||
if (!room) { |
||||
room = createRoom('c', channel, channelsAdmin, [], false); |
||||
if (!room || !room.rid) { |
||||
logger.error(`could not create channel ${ channel }`); |
||||
return; |
||||
} |
||||
} |
||||
if (Array.isArray(groupsFromSSO) && groupsFromSSO.includes(ssoGroup)) { |
||||
addUserToRoom(room._id, user); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static updateRolesFromSSO(user: Record<string, any>, identity: Record<string, any>, roleClaimName: string): void { |
||||
if (user && identity && roleClaimName) { |
||||
const rolesFromSSO = this.mapRolesFromSSO(identity, roleClaimName); |
||||
|
||||
if (!Array.isArray(user.roles)) { |
||||
user.roles = []; |
||||
} |
||||
|
||||
const toRemove = user.roles.filter((val: any) => !rolesFromSSO.includes(val)); |
||||
|
||||
// loop through roles that user has that sso doesnt have and remove each one
|
||||
toRemove.forEach(function(role: any) { |
||||
removeUserFromRoles(user._id, role); |
||||
}); |
||||
|
||||
const toAdd = rolesFromSSO.filter((val: any) => !user.roles.includes(val)); |
||||
|
||||
// loop through sso roles and add the new ones
|
||||
toAdd.forEach(function(role: any) { |
||||
addUserRoles(user._id, role); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
// Returns list of roles from SSO identity
|
||||
static mapRolesFromSSO(identity: Record<string, any>, roleClaimName: string): string[] { |
||||
let roles: string[] = []; |
||||
if (identity && roleClaimName) { |
||||
// Adding roles
|
||||
if (identity[roleClaimName] && Array.isArray(identity[roleClaimName])) { |
||||
roles = identity[roleClaimName].filter((val: string) => val !== 'offline_access' && val !== 'uma_authorization' && Roles.findOneByIdOrName(val)); |
||||
} |
||||
} |
||||
|
||||
return roles; |
||||
} |
||||
} |
Loading…
Reference in new issue