opensipscli 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opensipscli/__init__.py +20 -0
- opensipscli/args.py +56 -0
- opensipscli/cli.py +472 -0
- opensipscli/comm.py +57 -0
- opensipscli/config.py +162 -0
- opensipscli/db.py +989 -0
- opensipscli/defaults.py +91 -0
- opensipscli/libs/__init__.py +20 -0
- opensipscli/libs/sqlalchemy_utils.py +244 -0
- opensipscli/logger.py +85 -0
- opensipscli/main.py +86 -0
- opensipscli/module.py +69 -0
- opensipscli/modules/__init__.py +24 -0
- opensipscli/modules/database.py +1062 -0
- opensipscli/modules/diagnose.py +1089 -0
- opensipscli/modules/instance.py +53 -0
- opensipscli/modules/mi.py +200 -0
- opensipscli/modules/tls.py +354 -0
- opensipscli/modules/trace.py +292 -0
- opensipscli/modules/trap.py +138 -0
- opensipscli/modules/user.py +281 -0
- opensipscli/version.py +22 -0
- opensipscli-0.3.1.data/scripts/opensips-cli +9 -0
- opensipscli-0.3.1.dist-info/LICENSE +674 -0
- opensipscli-0.3.1.dist-info/METADATA +225 -0
- opensipscli-0.3.1.dist-info/RECORD +28 -0
- opensipscli-0.3.1.dist-info/WHEEL +5 -0
- opensipscli-0.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##
|
|
3
|
+
## This file is part of OpenSIPS CLI
|
|
4
|
+
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
5
|
+
##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify
|
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
## (at your option) any later version.
|
|
10
|
+
##
|
|
11
|
+
## This program is distributed in the hope that it will be useful,
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
## GNU General Public License for more details.
|
|
15
|
+
##
|
|
16
|
+
## You should have received a copy of the GNU General Public License
|
|
17
|
+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
from opensipscli.module import Module
|
|
21
|
+
from opensipscli.logger import logger
|
|
22
|
+
from opensipscli.config import cfg
|
|
23
|
+
from opensipscli.db import (
|
|
24
|
+
osdb, osdbError
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import getpass
|
|
29
|
+
import hashlib
|
|
30
|
+
|
|
31
|
+
DEFAULT_DB_NAME = "opensips"
|
|
32
|
+
USER_TABLE = "subscriber"
|
|
33
|
+
USER_NAME_COL = "username"
|
|
34
|
+
USER_DOMAIN_COL = "domain"
|
|
35
|
+
USER_PASS_COL = "password"
|
|
36
|
+
USER_HA1_COL = "ha1"
|
|
37
|
+
USER_HA1B_COL = "ha1b"
|
|
38
|
+
USER_HA1_SHA256_COL = "ha1_sha256"
|
|
39
|
+
USER_HA1_SHA512T256_COL = "ha1_sha512t256"
|
|
40
|
+
USER_RPID_COL = "rpid"
|
|
41
|
+
|
|
42
|
+
class user(Module):
|
|
43
|
+
|
|
44
|
+
def user_db_connect(self):
|
|
45
|
+
engine = osdb.get_db_engine()
|
|
46
|
+
|
|
47
|
+
db_url = cfg.read_param(["database_user_url", "database_url"],
|
|
48
|
+
"Please provide us the URL of the database")
|
|
49
|
+
if db_url is None:
|
|
50
|
+
print()
|
|
51
|
+
logger.error("no URL specified: aborting!")
|
|
52
|
+
return None, None
|
|
53
|
+
|
|
54
|
+
db_url = osdb.set_url_driver(db_url, engine)
|
|
55
|
+
db_name = cfg.read_param(["database_user_name", "database_name"],
|
|
56
|
+
"Please provide the database to add user to", DEFAULT_DB_NAME)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
db = osdb(db_url, db_name)
|
|
60
|
+
except osdbError:
|
|
61
|
+
logger.error("failed to connect to database %s", db_name)
|
|
62
|
+
return None, None
|
|
63
|
+
|
|
64
|
+
if not db.connect():
|
|
65
|
+
return None, None
|
|
66
|
+
|
|
67
|
+
res = db.find('version', 'table_version', {'table_name': USER_TABLE})
|
|
68
|
+
if not res:
|
|
69
|
+
osips_ver = '3.2+'
|
|
70
|
+
else:
|
|
71
|
+
# RFC 8760 support was introduced in OpenSIPS 3.2 (table ver 8+)
|
|
72
|
+
tb_ver = res.first()[0]
|
|
73
|
+
if tb_ver >= 8:
|
|
74
|
+
osips_ver = '3.2+'
|
|
75
|
+
else:
|
|
76
|
+
osips_ver = '3.1'
|
|
77
|
+
|
|
78
|
+
return db, osips_ver
|
|
79
|
+
|
|
80
|
+
def user_get_domain(self, name):
|
|
81
|
+
s = name.split('@')
|
|
82
|
+
if len(s) > 2:
|
|
83
|
+
logger.warning("invalid username {}".
|
|
84
|
+
format(name))
|
|
85
|
+
return None
|
|
86
|
+
elif len(s) == 1:
|
|
87
|
+
domain = cfg.read_param("domain",
|
|
88
|
+
"Please provide the domain of the user")
|
|
89
|
+
if not domain:
|
|
90
|
+
logger.warning("no domain specified for {}".
|
|
91
|
+
format(name))
|
|
92
|
+
return None
|
|
93
|
+
return name, domain
|
|
94
|
+
return s[0], s[1]
|
|
95
|
+
|
|
96
|
+
def user_get_password(self):
|
|
97
|
+
while True:
|
|
98
|
+
pw1 = getpass.getpass("Please enter new password: ")
|
|
99
|
+
pw2 = getpass.getpass("Please repeat the password: ")
|
|
100
|
+
if pw1 != pw2:
|
|
101
|
+
logger.warning("passwords are not the same! Please retry...")
|
|
102
|
+
else:
|
|
103
|
+
return pw1
|
|
104
|
+
|
|
105
|
+
def user_get_ha1(self, user, domain, password):
|
|
106
|
+
string = "{}:{}:{}".format(user, domain, password)
|
|
107
|
+
return hashlib.md5(string.encode('utf-8')).hexdigest()
|
|
108
|
+
|
|
109
|
+
def user_get_ha1b(self, user, domain, password):
|
|
110
|
+
string = "{}@{}:{}:{}".format(user, domain, domain, password)
|
|
111
|
+
return hashlib.md5(string.encode('utf-8')).hexdigest()
|
|
112
|
+
|
|
113
|
+
def user_get_ha1_sha256(self, user, domain, password):
|
|
114
|
+
string = "{}:{}:{}".format(user, domain, password)
|
|
115
|
+
return hashlib.sha256(string.encode('utf-8')).hexdigest()
|
|
116
|
+
|
|
117
|
+
def user_get_ha1_sha512t256(self, user, domain, password):
|
|
118
|
+
string = "{}:{}:{}".format(user, domain, password)
|
|
119
|
+
try:
|
|
120
|
+
o = hashlib.new("sha512-256")
|
|
121
|
+
except ValueError:
|
|
122
|
+
# SHA-512/256 is only available w/ OpenSSL 1.1.1 (Sep 2018) or
|
|
123
|
+
# newer, so let's just leave the field blank if we get an exception
|
|
124
|
+
logger.error(("The SHA-512/256 hashing algorithm is "
|
|
125
|
+
"apparently not available!?"))
|
|
126
|
+
logger.error("Adding user, but with a blank '{}' column!".format(
|
|
127
|
+
USER_HA1_SHA512T256_COL))
|
|
128
|
+
logger.error("Tip: installing OpenSSL 1.1.1+ should fix this")
|
|
129
|
+
return ""
|
|
130
|
+
|
|
131
|
+
o.update(string.encode('utf-8'))
|
|
132
|
+
return o.hexdigest()
|
|
133
|
+
|
|
134
|
+
def do_add(self, params=None, modifiers=None):
|
|
135
|
+
|
|
136
|
+
if len(params) < 1:
|
|
137
|
+
name = cfg.read_param(None,
|
|
138
|
+
"Please provide the username you want to add")
|
|
139
|
+
if not name:
|
|
140
|
+
logger.warning("no username to add!")
|
|
141
|
+
return -1
|
|
142
|
+
else:
|
|
143
|
+
name = params[0]
|
|
144
|
+
username, domain = self.user_get_domain(name)
|
|
145
|
+
|
|
146
|
+
db, osips_ver = self.user_db_connect()
|
|
147
|
+
if not db:
|
|
148
|
+
return -1
|
|
149
|
+
|
|
150
|
+
insert_dict = {
|
|
151
|
+
USER_NAME_COL: username,
|
|
152
|
+
USER_DOMAIN_COL: domain
|
|
153
|
+
}
|
|
154
|
+
# check if the user already exists
|
|
155
|
+
if db.entry_exists(USER_TABLE, insert_dict):
|
|
156
|
+
logger.error("User {}@{} already exists".
|
|
157
|
+
format(username, domain))
|
|
158
|
+
return -1
|
|
159
|
+
|
|
160
|
+
if len(params) > 1:
|
|
161
|
+
password = params[1]
|
|
162
|
+
else:
|
|
163
|
+
password = self.user_get_password()
|
|
164
|
+
if password is None:
|
|
165
|
+
logger.error("password not specified: cannot add user {}@{}".
|
|
166
|
+
format(user, domain))
|
|
167
|
+
return -1
|
|
168
|
+
insert_dict[USER_HA1_COL] = \
|
|
169
|
+
self.user_get_ha1(username, domain, password)
|
|
170
|
+
|
|
171
|
+
# only populate the 'ha1b' column on 3.1 or older OpenSIPS DBs
|
|
172
|
+
if osips_ver < '3.2':
|
|
173
|
+
insert_dict[USER_HA1B_COL] = \
|
|
174
|
+
self.user_get_ha1b(username, domain, password)
|
|
175
|
+
else:
|
|
176
|
+
insert_dict[USER_HA1_SHA256_COL] = \
|
|
177
|
+
self.user_get_ha1_sha256(username, domain, password)
|
|
178
|
+
insert_dict[USER_HA1_SHA512T256_COL] = \
|
|
179
|
+
self.user_get_ha1_sha512t256(username, domain, password)
|
|
180
|
+
|
|
181
|
+
insert_dict[USER_PASS_COL] = \
|
|
182
|
+
password if cfg.getBool("plain_text_passwords") else ""
|
|
183
|
+
|
|
184
|
+
db.insert(USER_TABLE, insert_dict)
|
|
185
|
+
logger.info("Successfully added {}@{}".format(username, domain))
|
|
186
|
+
|
|
187
|
+
db.destroy()
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
def do_password(self, params=None, modifiers=None):
|
|
191
|
+
|
|
192
|
+
if len(params) < 1:
|
|
193
|
+
name = cfg.read_param(None,
|
|
194
|
+
"Please provide the username to change the password for")
|
|
195
|
+
if not name:
|
|
196
|
+
logger.error("empty username")
|
|
197
|
+
return -1
|
|
198
|
+
else:
|
|
199
|
+
name = params[0]
|
|
200
|
+
username, domain = self.user_get_domain(name)
|
|
201
|
+
|
|
202
|
+
db, osips_ver = self.user_db_connect()
|
|
203
|
+
if not db:
|
|
204
|
+
return -1
|
|
205
|
+
|
|
206
|
+
user_dict = {
|
|
207
|
+
USER_NAME_COL: username,
|
|
208
|
+
USER_DOMAIN_COL: domain
|
|
209
|
+
}
|
|
210
|
+
# check if the user already exists
|
|
211
|
+
if not db.entry_exists(USER_TABLE, user_dict):
|
|
212
|
+
logger.warning("User {}@{} does not exist".
|
|
213
|
+
format(username, domain))
|
|
214
|
+
return -1
|
|
215
|
+
|
|
216
|
+
if len(params) > 1:
|
|
217
|
+
password = params[1]
|
|
218
|
+
else:
|
|
219
|
+
password = self.user_get_password()
|
|
220
|
+
if password is None:
|
|
221
|
+
logger.error("Password not specified: " +
|
|
222
|
+
"cannot change passowrd for user {}@{}".
|
|
223
|
+
format(user, domain))
|
|
224
|
+
return -1
|
|
225
|
+
plain_text_pw = cfg.getBool("plain_text_passwords")
|
|
226
|
+
update_dict = {
|
|
227
|
+
USER_HA1_COL: self.user_get_ha1(username, domain, password),
|
|
228
|
+
USER_PASS_COL: password if plain_text_pw else ""
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if osips_ver < '3.2':
|
|
232
|
+
update_dict[USER_HA1B_COL] = self.user_get_ha1b(
|
|
233
|
+
username, domain, password)
|
|
234
|
+
|
|
235
|
+
db.update(USER_TABLE, update_dict, user_dict)
|
|
236
|
+
logger.info("Successfully changed password for {}@{}".
|
|
237
|
+
format(username, domain))
|
|
238
|
+
db.destroy()
|
|
239
|
+
return True
|
|
240
|
+
|
|
241
|
+
def do_delete(self, params=None, modifiers=None):
|
|
242
|
+
|
|
243
|
+
if len(params) < 1:
|
|
244
|
+
name = cfg.read_param(None,
|
|
245
|
+
"Please provide the username you want to delete")
|
|
246
|
+
if not name:
|
|
247
|
+
logger.warning("no username to delete!")
|
|
248
|
+
return -1
|
|
249
|
+
else:
|
|
250
|
+
name = params[0]
|
|
251
|
+
username, domain = self.user_get_domain(name)
|
|
252
|
+
|
|
253
|
+
db, _ = self.user_db_connect()
|
|
254
|
+
if not db:
|
|
255
|
+
return -1
|
|
256
|
+
|
|
257
|
+
delete_dict = {
|
|
258
|
+
USER_NAME_COL: username,
|
|
259
|
+
USER_DOMAIN_COL: domain
|
|
260
|
+
}
|
|
261
|
+
# check if the user already exists
|
|
262
|
+
if not db.entry_exists(USER_TABLE, delete_dict):
|
|
263
|
+
logger.error("User {}@{} does not exist".
|
|
264
|
+
format(username, domain))
|
|
265
|
+
return -1
|
|
266
|
+
|
|
267
|
+
db.delete(USER_TABLE, delete_dict)
|
|
268
|
+
logger.info("Successfully deleted {}@{}".format(username, domain))
|
|
269
|
+
|
|
270
|
+
db.destroy()
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
def __exclude__(self):
|
|
274
|
+
if cfg.exists("dababase_user_url"):
|
|
275
|
+
db_url = cfg.get("database_user_url")
|
|
276
|
+
elif cfg.exists("database_url"):
|
|
277
|
+
db_url = cfg.get("database_url")
|
|
278
|
+
else:
|
|
279
|
+
return (not osdb.has_sqlalchemy(), None)
|
|
280
|
+
return (not osdb.has_dialect(osdb.get_dialect(db_url)), None)
|
|
281
|
+
|
opensipscli/version.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##
|
|
3
|
+
## This file is part of OpenSIPS CLI
|
|
4
|
+
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
5
|
+
##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify
|
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
## (at your option) any later version.
|
|
10
|
+
##
|
|
11
|
+
## This program is distributed in the hope that it will be useful,
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
## GNU General Public License for more details.
|
|
15
|
+
##
|
|
16
|
+
## You should have received a copy of the GNU General Public License
|
|
17
|
+
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
__version__ = '0.3.1'
|
|
21
|
+
|
|
22
|
+
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|