Skip to content

Commit 4ffd6c9

Browse files
authored
Merge pull request #226 from dedeck/master
Create deactivate_users.py
2 parents 83ef70b + 0012865 commit 4ffd6c9

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

examples/client/deactivate_users.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
'''
2+
Purpose: Deactivates Black Duck users who are not actively using the system, will warn for users that have never logged in.
3+
4+
Usage:
5+
deactivate_users.py [--dry-run] [--since-days DAYS] [--interactive] [--base-url https://your.blackduck.url] [--token-file token.txt]
6+
7+
required arguments:
8+
--base-url BASE_URL Hub server URL e.g. https://your.blackduck.url
9+
--token-file TOKEN_FILE containing access token
10+
11+
optional arguments:
12+
--dry-run Show actions that would be executed on a normal run
13+
--since-days Number of days since last login for a user to be inactive. Default: 90
14+
--interactive Run in interactive mode to choose which users to deactivate
15+
16+
Examples:
17+
18+
authentication required for all examples below
19+
--base-url https://your.blackduck.url --token-file token.txt
20+
21+
print help message
22+
python deactivate_users.py -h
23+
24+
deactivate all users who haven't logged in for more than 90 days
25+
python deactivate_users.py
26+
27+
log users that would be deactivated who haven't logged in for more than 90 days
28+
python deactivate_users.py --dry-run
29+
30+
deactivate all users who haven't logged in for more than 30 days
31+
python deactivate_users.py --since-days 30
32+
33+
interactively deactivate users who haven't logged in for more than 120 days
34+
python deactivate_users.py --since-days 120 --interactive
35+
'''
36+
37+
from blackduck import Client
38+
import logging
39+
import argparse
40+
41+
def query_yes_no(question, default="yes"):
42+
"""Ask a yes/no question via input() and return their answer.
43+
44+
"question" is a string that is presented to the user.
45+
"default" is the presumed answer if the user just hits <Enter>.
46+
It must be "yes" (the default), "no" or None (meaning
47+
an answer is required of the user).
48+
49+
The "answer" return value is True for "yes" or False for "no".
50+
"""
51+
valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False}
52+
if default is None:
53+
prompt = " [y/n] "
54+
elif default == "yes":
55+
prompt = " [Y/n] "
56+
elif default == "no":
57+
prompt = " [y/N] "
58+
else:
59+
raise ValueError("invalid default answer: '%s'" % default)
60+
61+
while True:
62+
print(question + prompt)
63+
choice = input().lower()
64+
if default is not None and choice == "":
65+
return valid[default]
66+
elif choice in valid:
67+
return valid[choice]
68+
else:
69+
print("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n")
70+
71+
logging.basicConfig(
72+
level=logging.INFO,
73+
format="[%(asctime)s] {%(module)s:%(lineno)d} %(levelname)s - %(message)s"
74+
)
75+
76+
# Use -h to print help message
77+
parser = argparse.ArgumentParser("Deactivates users in the system")
78+
parser.add_argument("--base-url", required=True, help="Hub server URL e.g. https://your.blackduck.url")
79+
parser.add_argument("--token-file", dest='token_file', required=True, help="containing access token")
80+
parser.add_argument("--no-verify", dest='verify', action='store_false', help="disable TLS certificate verification")
81+
parser.add_argument("-d", "--dry-run", action='store_true', help=f"Run in dry-run mode to determine users it will update")
82+
parser.add_argument("-s", "--since-days", default=90, type=int, help=f"Number of days since last login for a user to be inactive. Default: 90")
83+
parser.add_argument("-i", "--interactive", action='store_true', help=f"Run in interactive mode to manually choose which users to deactivate, has no effect when run with dry-run mode")
84+
85+
args = parser.parse_args()
86+
87+
with open(args.token_file, 'r') as tf:
88+
access_token = tf.readline().strip()
89+
90+
bd = Client(
91+
base_url=args.base_url,
92+
token=access_token,
93+
verify=args.verify
94+
)
95+
96+
system_users = ['sysadmin', 'anonymous', 'blackduck_system', 'default-authenticated-user']
97+
98+
dormant_params = {
99+
"sinceDays": args.since_days
100+
}
101+
102+
headers = {
103+
'accept': "application/vnd.blackducksoftware.user-4+json"
104+
}
105+
106+
for user in bd.get_items("api/dormant-users", params=dormant_params, headers=headers):
107+
if user['username'] not in system_users:
108+
# If the user has logged in before
109+
if 'lastLogin' in user:
110+
if args.dry_run:
111+
logging.info(f"Will mark user '{user['username']}' as inactive, their last login date was {user['lastLogin']}")
112+
else:
113+
114+
user_url = user['_meta']['href'].replace("/last-login", "")
115+
116+
# Get the user data to keep all data the same except for the active parameter
117+
user_data = bd.get_json(user_url)
118+
# Skip already inactive users
119+
if user_data['active'] == False:
120+
continue
121+
122+
# If interactive mode is running, prompt the user before disabling
123+
if args.interactive:
124+
proceed = query_yes_no(f"User '{user['username']}' last login date was {user['lastLogin']}, do you want to mark them as inactive?")
125+
if not proceed:
126+
logging.info(f"Skipping user '{user['username']}' due to interactive input")
127+
continue
128+
logging.info(f"Marking user '{user['username']}' as inactive")
129+
deactivate_params = {"userName": user_data['userName'],
130+
"externalUserName": user_data['externalUserName'] if 'externalUserName' in user_data else None,
131+
"firstName": user_data['firstName'],
132+
"lastName": user_data['lastName'],
133+
"email": user_data['email'],
134+
"type": user_data['type'],
135+
"active": False}
136+
# Deactivate the user
137+
response = bd.session.put(user_url, json=deactivate_params)
138+
if response.status_code == 200:
139+
logging.info(f"User '{user['username']}' updated successfully")
140+
elif response.status_code == 404:
141+
logging.info(f"User '{user['username']}' 404 Not found")
142+
else:
143+
logging.error(f"Unexpected error updating user '{user['username']}'")
144+
bd.http_error_handler(response)
145+
#Else the user has never logged in
146+
else:
147+
logging.warning(f"User '{user['username']}' has never logged in")

0 commit comments

Comments
 (0)