knktc's Notes

python, cloud, linux...

0%

Bulk Disable Confluence Users with Selenium

I recently wanted to clean up a batch of former employees from Confluence so we could free up some licenses. It turned out that Confluence did not seem to provide a built-in bulk cleanup feature, and the official REST API also did not expose a disable-user endpoint. So I ended up learning a bit of Selenium and wrote a small script to do it myself.

In Confluence, there are usually two ways to free up consumed licenses:

  • Disable the user
  • Remove the user from all groups

In most cases, simply disabling the user is enough. But if the same user exists in multiple user directories, such as both Confluence internal auth and external LDAP, then removing the user from all groups may still be necessary.

I wrote the following script and tested it with Confluence 7.11.0 and Chrome 88.

Gist:

https://gist.github.com/knktc/b7e558ba77973f04f4c580d6ecbe91ba

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env python3

"""
A simple script to Bulk disable Confluence users with Selenium
Tested with Confluence 7.11.0 and Chrome 88
"""

import time
from selenium import webdriver
from urllib.parse import urljoin


CONF = {
'base_url': 'https://YOUR_CONFLUENCE_BASE_URL',
'c_username': 'YOUR_ADMIN_USERNAME',
'c_password': 'YOUR_ADMIN_PASSWORD',
}
USERS = [
'USERNAME_1',
'USERNAME_2',
'USERNAME_3',
]


class BulkUserOperator:
def __init__(self, conf: dict):
self.browser = webdriver.Chrome()
self.conf = conf
self.base_url = conf['base_url']

def __exit__(self, exc_type, exc_val, exc_tb):
self.browser.close()

def login(self):
browser = self.browser
username = self.conf['c_username']
password = self.conf['c_password']

browser.get(urljoin(self.base_url, '/admin/viewgeneralconfig.action'))
browser.find_element_by_id('os_username').send_keys(username)
browser.find_element_by_id('os_password').send_keys(password)
browser.find_element_by_id('loginButton').click()
time.sleep(2)

browser.find_element_by_id('password').send_keys(password)
browser.find_element_by_id('authenticateButton').click()
time.sleep(2)

def disable_user(self, username: str):
browser = self.browser
url = urljoin(self.base_url, f'/admin/users/deactivateuser.action?username={username}')
browser.get(url)

try:
elem = browser.find_element_by_id('confirm')
elem.click()
except:
time.sleep(2)
return False, 'no such user'

time.sleep(2)
return True, None

def remove_groups(self, username: str):
browser = self.browser
url = urljoin(self.base_url, f'/admin/users/editusergroups-start.action?username={username}')
browser.get(url)

try:
browser.find_element_by_id('editusergroups-selectnone').click()
time.sleep(1)
browser.find_element_by_id('save-btn1').click()
except:
time.sleep(2)
return False, 'no such user'

time.sleep(2)
return True, None


def main():
operator = BulkUserOperator(CONF)
operator.login()

for user in USERS:
status, msg = operator.disable_user(user)
if status:
status, msg = operator.remove_groups(user)
if status:
print(f'user [{user}] has been disabled and all groups removed')
else:
print(f'user [{user}] has been disabled but remove groups failed')
else:
print(f'user: [{user}] not exists')
continue


if __name__ == '__main__':
main()
如果我的文字帮到了您,那么可不可以请我喝罐可乐?