LDAP Auth - Disabled AD Users

Hello,

We have had Rocket.Chat configured with LDAP authentication back to our AD domain without issue for some time. I just recently came to notice when users are disabled in Active Directory, they are still able to login to Rocket.Chat. Is this expected or am I doing something wrong? I’ve poured over the settings in the admin area but can find any pertaining to this.

Other applications that we have LDAP auth with like NextCloud or Zabbix prevent users from signing in when their AD accounts are disabled, as I would expect.

EDIT: Also, I’ve noticed if you LDAP sync a user that has been disabled, it will not let you login until you enable in AD Users and Computers and re-sync LDAP. However, for existing users, it doesn’t sync or pick up that the user is disabled.

Thanks

1 Like

We have the same issue/need

I never could find a solution to make Rocket.Chat automatically disable users when the AD user is disabled, so I just added this to my PowerShell script that I already use to disable AD users. It will deactivate the Rocket.Chat user for me:

$user = "your_username_here"
$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$Headers.Add("X-Auth-Token", 'my-auth-token')
$Headers.Add("X-User-ID", 'my-user-id')
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$response = Invoke-RestMethod -Headers $Headers  https://my.chatserver.com/api/v1/users.list
$RC_id = ($response.users | Where-Object {$_.username -eq "$user"})._id
$body = @{
    userId = "$RC_id"
    data = @{
        "active" = $false
    }
    }

$body = $body | ConvertTo-Json -Depth 2
$DisableResponse = Invoke-RestMethod -Headers $Headers https://my.chatserver.com/api/v1/users.update -Method Post -Body $body -ContentType 'application/json' 

Maybe this will help you too.

2 Likes

Amazing! Thanks :blush: Will try it out & see if it’ll shout people up with the moaning :joy:

this is one reason I havne’t been able to deploy rocket chat past a POC. I hope the devs take notice and feel like this is an issue that can be looked at.

It’s a minor inconvenience to deactivate the user, but I already had a disable AD user script. So adding what I did above wasn’t too bad. However, I still think it should do it automatically like many other services with LDAP integration do.

Id say its a higher issue than minor and would rate this a med to high level issue. The reason for integrating with a central directory, it be ldap or something else, is so they can manage from that directory. The assumption is and should be, if I disable or delete a user from LDAP, then that user is disabled in every single ldap integrated application. Best case, a person gets in trouble or fails an audit because users were still able to sign into an application that you believed them to be disabled in. Worse case a user believed to be disabled can still access sensitive information from outside the organization, and then uses that information in a way that hurts the organization. If rocket chat is going to be an internet facing “cloud” product…which allows collaborative communication and file sharing or potential sensitive information…than something like this must be addressed. At the very least as warning needs to be issued on the setup page when configuring LDAP.

Agreed on the warning in the LDAP config area. It looks like it is expected behavior at this point: https://github.com/RocketChat/Rocket.Chat/issues/9916

Thanks for this! I used your code to loop all disabled users and set task scheduler for the script.

$useri = Get-ADUser -SearchBase "OU=disabled,OU=example,DC=contoso,DC=com" -Filter *

foreach ($user in $useri)
{
$u = $user.SamAccountName
$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$Headers.Add("X-Auth-Token", 'yourtoken')
$Headers.Add("X-User-ID", 'yourid')
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$response = Invoke-RestMethod -Headers $Headers  https://chat.contoso.com/api/v1/users.list
$RC_id = ($response.users | Where-Object {$_.username -eq "$u"})._id
$body = @{
    userId = "$RC_id"
    data = @{
        "active" = $false
    }
    }

$body = $body | ConvertTo-Json -Depth 2
$DisableResponse = Invoke-RestMethod -Headers $Headers https://chat.contoso.com/api/v1/users.update -Method Post -Body $body -ContentType 'application/json' 
}

For those, like me, who come here to find a way to disable Rocket.Chat users which are not existing within openLDAP, I have written a ruby script to sync that correctly, just install the rubygems net-ldap and rocketchat and put this script into some executeable path:

#!/usr/bin/env ruby

require 'net-ldap'
require 'rocketchat'

options = {
    ldap: {
        host: ENV.fetch('LDAP_HOST', 'ldap.example.com'),
        port: ENV.fetch('LDAP_PORT', '636').to_i,
        base: ENV.fetch('LDAP_BASE', 'ou=People,dc=example,dc=com'),
        username: ENV.fetch('LDAP_USERNAME', 'uid=rocketchat,ou=People,dc=example,dc=com'),
        password: ENV.fetch('LDAP_PASSWORD', 'p455w0rd'),
    },
    chat: {
        url: ENV.fetch('CHAT_URL', 'https://rocketchat.example.com'),
        username: ENV.fetch('CHAT_USERNAME', 'username'),
        password: ENV.fetch('CHAT_PASSWORD', 'p455w0rd'),
        delete: ENV.fetch('CHAT_DELETE', 'false') == 'true'
    }
}

available = []

begin
    Net::LDAP.new(
        host: options[:ldap][:host],
        port: options[:ldap][:port],
        encryption: :simple_tls,
        base: options[:ldap][:base],
        auth: {
            method: :simple,
            username: options[:ldap][:username],
            password: options[:ldap][:password]
        }
    ).tap do |ldap|
        filter = Net::LDAP::Filter.eq(
            'objectClass',
            'posixAccount'
        )

        ldap.search(
            base: options[:ldap][:base],
            filter: filter,
            attributes: ['cn'],
            return_result: false
        ) do |entry|
            available.push entry.cn.first
        end
    end
rescue
    puts "failed to connect to ldap"
    exit 1
end

if available.empty?
    puts "failed to fetch ldap users"
    exit 1
end

RocketChat::Server.new(options[:chat][:url]).tap do |server|
    session = begin
        server.login(
            options[:chat][:username],
            options[:chat][:password]
        )
    rescue
        puts "failed to auth rocketchat"
        exit 1
    end

    begin
        offset = 0

        loop do
            users = session.users.list(offset: offset, count: 50, query: { ldap: true })
            break if users.empty?

            users.each do |user|
                next if available.include? user.username

                if options[:chat][:delete]
                    record = session.users.delete(
                        user.id,
                    )

                    puts "deleted inactive user #{user.username} with id #{user.id}"
                else
                    next unless user.active?

                    record = session.users.update(
                        user.id,
                        active: false
                    )

                    puts "disabled inactive user #{user.username} with id #{user.id}"
                end
            end

            offset += users.length
        end
    ensure
        session.logout
    end
end
1 Like

I want to share my scripts to deactivate RC users.

function Get-RCUserId
{
	param(
		    [Parameter(Mandatory=$True)][string]$Username
	)
    $username = $username.ToLower().Trim()
    $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $Headers.Add("X-Auth-Token", 'token')
    $Headers.Add("X-User-ID", 'userId')
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $url = "https://rc.company.com/api/v1/users.info?username=" + $username
    
    try {
        $Response = Invoke-RestMethod -Headers $Headers $url -Method Get -ContentType 'application/json'
        $result = $Response.user._id
    } catch {
        $result = $_.Exception
    }
                 
    return $result
}

function Disable-RCUser
{
	param(
		    [Parameter(Mandatory=$True)][string]$userId
	)

    $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $Headers.Add("X-Auth-Token", 'token')
    $Headers.Add("X-User-ID", 'userId')
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $body = @{
        userId = $userId
        data = @{ "active" = $false }
    }
    $body = $body | ConvertTo-Json -Depth 2
    try {
        $DisableResponse = Invoke-RestMethod -Headers $Headers "https://rc.company.com/api/v1/users.update" -Method Post -Body $body -ContentType 'application/json'             
        $result = $DisableResponse.success
    } catch {
        $result = $_.Exception
    }
    return $result
}

$UsertoDisable = Get-RCUserId -Username "TestUser"
Disable-RCUser -userId $UsertoDisable

1 Like

I’ve changed your function to dedicated script with support different codepages with UTF8 and to return full user object.

get-RCuser.ps1:

param([Parameter(Mandatory=$True)][string]$Username,$connect_url,$connect_id,$connect_token)

# <This block is for getting settings from set-RCAuth.ps1 and can be commented out.
$script_fullname = $MyInvocation.MyCommand.Definition	
$script_folder = split-path -parent $script_fullname
$authpath="$script_folder\set-RCAuth.ps1"
.("$authpath")
# If this block is commented out then use script parameters>
if (!$connect_url -or !$connect_id -or !$connect_token) {return "use all parameters!"}

$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$Headers.Add("X-Auth-Token", $connect_token)
$Headers.Add("X-User-ID", $connect_id)

$username = $username.ToLower().Trim()

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "$connect_url/api/v1/users.info?username=" + $username

$response = Invoke-WebRequest -Uri $url -UseBasicParsing -Headers $Headers
$reader = New-Object System.IO.StreamReader($response.RawContentStream, [System.Text.Encoding]::UTF8)
$result = ($reader.ReadToEnd()|Convertfrom-Json).user
             
return $result
1 Like

Another powershell script to list all RC users:

param ($connect_url,$connect_id,$connect_token)

# <This block is for getting settings from set-RCAuth.ps1 and can be commented out.
$script_fullname = $MyInvocation.MyCommand.Definition	
$script_folder = split-path -parent $script_fullname
$authpath="$script_folder\set-RCAuth.ps1"
.("$authpath")
# If this block is commented out then use script parameters>
if (!$connect_url -or !$connect_id -or !$connect_token) {return "use all parameters!"}

$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$Headers.Add("X-Auth-Token", $connect_token)
$Headers.Add("X-User-ID", $connect_id)

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "$connect_url/api/v1/users.list"

$user_list=@()
$user_page=100 #100 maximium
$user_offset=0
do {
	$response = Invoke-WebRequest -Uri "$($url)?count=$($user_page)&offset=$($user_offset)" -UseBasicParsing -Headers $Headers
	$reader = New-Object System.IO.StreamReader($response.RawContentStream, [System.Text.Encoding]::UTF8)
	$result = ($reader.ReadToEnd()|Convertfrom-Json).users
	if ($result.count){
		$user_list+=$result
		$user_offset+=$result.count #correct no need for +/-1
	}
}while($result.count -eq $user_page)

return $user_list

Very important update for all admins using that scripts to deactivate users


users.update - not a best choice for that
Here is a perfectly working PowerShell function
    function Deactivate-RCUser
    {
    	param(
    		    [Parameter(Mandatory=$True)][string]$userId
    	)

        $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
        $Headers.Add("X-Auth-Token", 'AdminToken')
        $Headers.Add("X-User-ID", 'AdminID')
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        $body = @{
            userId = $userId
            activeStatus = $false
            confirmRelinquish = $true
        }
        $body = $body | ConvertTo-Json
        try {
            $DisableResponse = Invoke-RestMethod "https://rc.company.com/api/v1/users.setActiveStatus" -Headers $Headers -Method Post -Body $body -ContentType "application/json"
            $result = $DisableResponse.success
        } catch {
            $result = $_.Exception
        }
        return $result
    }

1 Like