This repository was archived by the owner on May 22, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlldap_init.py
More file actions
170 lines (129 loc) · 6.4 KB
/
Copy pathlldap_init.py
File metadata and controls
170 lines (129 loc) · 6.4 KB
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# Inspired from https://github.com/lldap/lldap/issues/654#issuecomment-1694251863
import json
import os
import subprocess
from qlient.http import HTTPBackend, HTTPClient, Fields
from requests import RequestException, Session
# set structures for graphQL
GROUP_FIELDS = Fields("id", "displayName")
USER_FIELDS = Fields("id", "email", "displayName", groups=GROUP_FIELDS)
# default configuration
ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD', "/config/admin_password")
CONFIG_FILE = os.environ.get('CONFIG_FILE', "/config/config_file.json")
def validate(expression: bool, message: str = "") -> None:
""" Validate, to be used like Assert """
if not expression:
print(message)
exit(1)
return
def validateConfiguration(configuration_file: str, admin_password_file: str) -> dict:
""" Validate and return configuration dict """
validate(os.path.isfile(admin_password_file) is True, "admin_password_file '" +
admin_password_file + "' does not exist...")
validate(os.path.isfile(configuration_file) is True, "config_file '" +
configuration_file + "' does not exist...")
with open(configuration_file) as config_file:
configuration: dict = json.load(config_file)
validate(len(configuration["admin_username"])
> 0, "admin_username not set...")
validate(len(configuration["ldap_url"]) > 0, "ldap_url not set...")
validate(len(configuration["web_url"]) > 0, "web_url not set...")
validate(len(configuration["base_dn"]) > 0, "base_dn not set...")
validate(len(configuration["seed"]) > 0, "seed not set...")
return configuration
def createauthenticatedWebClient(admin_username: str, web_url: str, admin_password_file: str) -> HTTPClient:
""" Log into LLDAP, and return the HTTPClient to be used for next requests """
session = Session()
with open(admin_password_file, "r") as f:
admin_password = f.read()
try:
jwt_token_request = session.post(
url=f'{web_url}/auth/simple/login',
json={
"username": admin_username,
"password": admin_password,
}
)
admin_password = ""
except RequestException as e:
raise SystemExit(e)
validate(jwt_token_request.status_code == 200, jwt_token_request.content)
jwt_token: str = jwt_token_request.json()["token"]
session.headers["Authorization"] = f"Bearer {jwt_token}"
httpclient = HTTPClient(HTTPBackend(
f'{web_url}/api/graphql', session=session))
return httpclient
def create_single_user(user: dict, ldap_url: str, base_dn: str, admin_username: str, admin_password_file: str, client: HTTPClient) -> json:
""" Create a user via graphql and set its password via ldappasswd """
groups = user.pop("groups", [])
password_file = user.pop("password_file")
# create user
req = client.mutation.createUser(user=user, _fields=USER_FIELDS)
validate(req.errors is None, req.raw)
# add to groups
for group in groups:
req = client.mutation.addUserToGroup(
userId=user["id"], groupId=group["id"])
validate(req.errors is None, req.raw)
# set password
ret = subprocess.run([
"ldappasswd",
"-x",
"-H", ldap_url,
"-D", "uid=" + admin_username + ",ou=people," + base_dn,
"-y", admin_password_file,
"-T", password_file,
"uid=" + user["id"] + ",ou=people," + base_dn
], capture_output=True)
validate(ret.returncode == 0, "Error setting password for '" +
user["id"] + "'.\n" + ret.stderr.decode("utf-8"))
return user
def create_all_groups(groups: dict, client: HTTPClient) -> dict:
""" Create all groups with graphql """
existing_groups: dict = client.query.groups(GROUP_FIELDS).data["groups"]
existing_groups_map: dict = {
group["displayName"]: group for group in existing_groups}
for must_exist_group in sorted(groups):
if must_exist_group not in existing_groups_map:
print(f"Group '{must_exist_group}' does not exist, creating...")
group: dict = client.mutation.createGroup(
name=must_exist_group, _fields=GROUP_FIELDS).data["createGroup"]
existing_groups_map[must_exist_group] = group
print(f"\tGroup '{must_exist_group}' created.")
else:
print(f"Group '{must_exist_group}' exists, skipping")
return existing_groups_map
def create_all_users(users: dict, existing_groups_map: dict, ldap_url: str, base_dn: str, admin_username: str, admin_password_file: str, client: HTTPClient) -> dict:
""" Create all users with graphql and ldappasswd"""
existing_users: dict = client.query.users(USER_FIELDS).data["users"]
existing_users_map: dict = {user["id"]: user for user in existing_users}
for must_exist_user in sorted(users, key=lambda x: x["id"]):
must_exist_user_id: str = must_exist_user["id"]
# Seed file groups is just a list of names, API returns id + displayName
must_exist_user["groups"][:] = [
existing_groups_map[group_name] for group_name in must_exist_user["groups"]
]
if must_exist_user_id not in existing_users_map:
print(f"User '{must_exist_user_id}' does not exist, creating...")
user = create_single_user(must_exist_user, ldap_url, base_dn,
admin_username, admin_password_file, client)
existing_users_map[must_exist_user_id] = user
print(f"\tUser '{must_exist_user_id}' created.")
else:
print(f"User '{must_exist_user_id}' exists, skipping")
return existing_users_map
def main() -> int:
"""Echo the input arguments to standard output"""
configuration = validateConfiguration(CONFIG_FILE, ADMIN_PASSWORD)
client = createauthenticatedWebClient(
configuration["admin_username"], configuration["web_url"], ADMIN_PASSWORD)
configuration_groups = configuration["seed"].get("groups", [])
configuration_users = configuration["seed"].get("users", [])
groups = create_all_groups(configuration_groups, client)
users = create_all_users(configuration_users, groups,
configuration["ldap_url"], configuration["base_dn"], configuration["admin_username"], ADMIN_PASSWORD, client)
print("Finished: " + str(len(users)) +
" users and " + str(len(groups)) + " groups.")
return 0
if __name__ == '__main__':
exit(main())