Skip to content

Commit d7eb6dd

Browse files
committed
tls: accept the domain and its type as named parameters too
Rejecting 'domain=' and 'type=' meant the identity of a row was the one thing that could not be written the way every other column is, which is hard to justify to someone who just read the column list. Take everything as 'column=value', keep the first two arguments as a shorthand for the domain and its type, and refuse only the combination of the two, which is the single ambiguous case. Whatever is left out is then asked for, in one place instead of one per spelling. This folds tls_db_domain() into tls_db_params(), so db_show and db_delete go through the same parser and now report the columns they do not take. 'id' stays refused: the database generates it.
1 parent 63af333 commit d7eb6dd

2 files changed

Lines changed: 72 additions & 53 deletions

File tree

‎docs/modules/tls.md‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ as paths to files. A TLS domain is identified by its name and its type
2020
```
2121
opensips-cli -x tls db_delete a.example.org server
2222
```
23-
The commands addressing a single domain ask for the type when it is not given.
24-
`db_add` and `db_show` default to `server`, while `db_update` and `db_delete`
25-
have no default and keep asking until one is given, so that they cannot change
26-
a different domain than the intended one.
23+
The domain and its type may also be given by name, in which case they can
24+
appear anywhere among the other columns:
25+
```
26+
opensips-cli -x tls db_delete domain=a.example.org type=server
27+
```
28+
Giving one of them both ways at once is an error. The commands addressing a
29+
single domain ask for whatever is left out; `db_add` and `db_show` default the
30+
type to `server`, while `db_update` and `db_delete` have no default and keep
31+
asking until one is given, so that they cannot change a different domain than
32+
the intended one.
2733

2834
`db_add` and `db_update` take the remaining `tls_mgm` columns as `column=value`
2935
arguments, in any order and after the domain and the type:

‎opensipscli/modules/tls.py‎

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
TLS_CALIST_COL = "ca_list"
3939
TLS_DH_COL = "dh_params"
4040

41-
# the tls_mgm columns that can be provisioned; 'id' is auto-incremented, while
42-
# 'domain' and 'type' identify the row and are passed as arguments
41+
# the tls_mgm columns that can be provisioned
4342
TLS_MGM_COLUMNS = ["match_ip_address", "match_sip_domain", "method",
4443
"verify_cert", "require_cert", TLS_CERT_COL, TLS_PK_COL,
4544
"crl_check_all", "crl_dir", TLS_CALIST_COL, "ca_dir", "cipher_list",
@@ -48,8 +47,11 @@
4847
# columns holding PEM content, which is read from the file they point to
4948
TLS_PEM_COLUMNS = [TLS_CERT_COL, TLS_PK_COL, TLS_CALIST_COL, TLS_DH_COL]
5049

51-
# columns identifying a row, which are not provisioned as 'column=value'
52-
TLS_ID_COLUMNS = ["id", TLS_DOMAIN_COL, TLS_TYPE_COL]
50+
# columns identifying a row, in the order they are accepted as arguments
51+
TLS_KEY_COLUMNS = [TLS_DOMAIN_COL, TLS_TYPE_COL]
52+
53+
# generated by the database, never provisioned
54+
TLS_ID_COL = "id"
5355

5456
# as defined by CLIENT_DOMAIN_TYPE/SERVER_DOMAIN_TYPE in tls_mgm/tls_domain.h
5557
TLS_DOMAIN_TYPES = {"client": 1, "server": 2}
@@ -411,37 +413,6 @@ def tls_db_connect(self):
411413

412414
return db
413415

414-
def tls_db_domain(self, params, require_type=False):
415-
"""
416-
resolves the (domain, type) pair identifying a tls_mgm row; commands
417-
that change an existing row get no default type, so that they cannot
418-
pick a different row than the intended one
419-
"""
420-
if len(params) > 0:
421-
domain = params[0]
422-
else:
423-
domain = cfg.read_param(None,
424-
"Please provide the name of the TLS domain")
425-
if not domain:
426-
logger.error("no TLS domain specified!")
427-
return None, None
428-
429-
if len(params) > 1:
430-
dtype = params[1]
431-
else:
432-
dtype = cfg.read_param(None, "TLS domain type (server/client)",
433-
None if require_type else "server")
434-
if not dtype:
435-
logger.error("no TLS domain type specified!")
436-
return None, None
437-
438-
if dtype.lower() not in TLS_DOMAIN_TYPES:
439-
logger.error("invalid TLS domain type '%s': "
440-
"expected 'server' or 'client'", dtype)
441-
return None, None
442-
443-
return domain, TLS_DOMAIN_TYPES[dtype.lower()]
444-
445416
def tls_db_reload(self):
446417
"""
447418
makes a running OpenSIPS pick up the tls_mgm changes
@@ -452,21 +423,21 @@ def tls_db_reload(self):
452423

453424
def tls_db_params(self, params, require_type=False):
454425
"""
455-
splits the params into the 'column=value' assignments and the
456-
(domain, type) pair identifying the row; the value of a PEM column is
457-
the path of the file holding it. The columns are parsed first, so that
458-
a bad one is reported without asking for the domain beforehand
426+
resolves the (domain, type) pair identifying a tls_mgm row, along with
427+
the columns to provision. Everything is given as 'column=value', with
428+
the domain and its type also accepted as the first two arguments; what
429+
is left out is asked for. The value of a PEM column is the path of the
430+
file holding it
459431
"""
460432
cols = {}
461433
for param in [p for p in params if '=' in p]:
462434
col, val = param.split('=', 1)
463-
if col in TLS_ID_COLUMNS:
464-
logger.error("column '%s' identifies the row and cannot be "
465-
"provisioned; the domain and its type are passed as "
466-
"arguments", col)
435+
if col == TLS_ID_COL:
436+
logger.error("column '%s' is generated by the database",
437+
TLS_ID_COL)
467438
return None, None, None
468439

469-
if col not in TLS_MGM_COLUMNS:
440+
if col not in TLS_MGM_COLUMNS and col not in TLS_KEY_COLUMNS:
470441
logger.error("unknown %s column '%s'", TLS_MGM_TABLE, col)
471442
return None, None, None
472443

@@ -486,12 +457,48 @@ def tls_db_params(self, params, require_type=False):
486457

487458
cols[col] = val
488459

489-
domain, dtype = self.tls_db_domain(
490-
[p for p in params if '=' not in p], require_type)
460+
# the domain and its type identify the row, they are not provisioned
461+
args = [p for p in params if '=' not in p]
462+
if len(args) > len(TLS_KEY_COLUMNS):
463+
logger.error("too many arguments: expected at most a domain and "
464+
"its type")
465+
return None, None, None
466+
467+
key = {}
468+
for i, col in enumerate(TLS_KEY_COLUMNS):
469+
if i < len(args):
470+
if col in cols:
471+
logger.error("'%s' given both as an argument and as "
472+
"'%s='", col, col)
473+
return None, None, None
474+
key[col] = args[i]
475+
else:
476+
key[col] = cols.pop(col, None)
477+
478+
domain = key[TLS_DOMAIN_COL]
491479
if not domain:
480+
domain = cfg.read_param(None,
481+
"Please provide the name of the TLS domain")
482+
if not domain:
483+
logger.error("no TLS domain specified!")
484+
return None, None, None
485+
486+
dtype = key[TLS_TYPE_COL]
487+
if not dtype:
488+
# commands changing an existing row get no default, so that they
489+
# cannot pick a different row than the intended one
490+
dtype = cfg.read_param(None, "TLS domain type (server/client)",
491+
None if require_type else "server")
492+
if not dtype:
493+
logger.error("no TLS domain type specified!")
494+
return None, None, None
495+
496+
if dtype.lower() not in TLS_DOMAIN_TYPES:
497+
logger.error("invalid TLS domain type '%s': "
498+
"expected 'server' or 'client'", dtype)
492499
return None, None, None
493500

494-
return domain, dtype, cols
501+
return domain, TLS_DOMAIN_TYPES[dtype.lower()], cols
495502

496503
def do_db_add(self, params=None, modifiers=None):
497504
"""
@@ -590,7 +597,10 @@ def do_db_show(self, params=None, modifiers=None):
590597
"""
591598
prints the columns of a TLS domain
592599
"""
593-
domain, dtype = self.tls_db_domain(params or [])
600+
domain, dtype, cols = self.tls_db_params(params or [])
601+
if domain and cols:
602+
logger.error("db_show takes no column: '%s'", list(cols)[0])
603+
return -1
594604
if not domain:
595605
return -1
596606

@@ -635,7 +645,10 @@ def do_db_delete(self, params=None, modifiers=None):
635645
"""
636646
removes a TLS domain from the database
637647
"""
638-
domain, dtype = self.tls_db_domain(params or [], True)
648+
domain, dtype, cols = self.tls_db_params(params or [], True)
649+
if domain and cols:
650+
logger.error("db_delete takes no column: '%s'", list(cols)[0])
651+
return -1
639652
if not domain:
640653
return -1
641654

0 commit comments

Comments
 (0)